@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.
- package/dist/bootstrap/web/session.d.ts +2 -0
- package/dist/core/auth/abilityChecker.d.ts +6 -0
- package/dist/core/auth/policy.d.ts +2 -2
- package/dist/core/database/baseRepository.d.ts +6 -3
- package/dist/core/database/boundConnection.d.ts +5 -0
- package/dist/core/database/index.d.ts +1 -0
- package/dist/core/database/query.d.ts +2 -1
- package/dist/core/database/repositoryConnection.d.ts +4 -0
- package/dist/core/database/repositoryQuery.d.ts +20 -0
- package/dist/core/http/requireAbilityMiddleware.d.ts +2 -2
- package/dist/index.js +21 -10
- package/package.json +2 -2
|
@@ -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>;
|
|
@@ -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:
|
|
13
|
-
authorize(resource: string, action:
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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,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
|
|
1
|
+
import type { AbilityChecker } from "../auth/abilityChecker";
|
|
2
2
|
import type { Middleware } from "./middleware";
|
|
3
|
-
declare function createRequireAbilityMiddleware(
|
|
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(
|
|
954
|
+
function createRequireAbilityMiddleware(abilityChecker) {
|
|
955
955
|
return (ability) => {
|
|
956
956
|
return async (_request, next) => {
|
|
957
957
|
const user = currentAuthUser();
|
|
958
958
|
try {
|
|
959
|
-
|
|
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
|
|
1512
|
-
const requireAbility = createRequireAbilityMiddleware(
|
|
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
|
|
1536
|
-
const requireAbility = createRequireAbilityMiddleware(
|
|
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 {
|
|
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.
|
|
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.
|
|
35
|
+
"@getstrata/core": "^0.4.0",
|
|
36
36
|
"typescript": "^5.9.0"
|
|
37
37
|
}
|
|
38
38
|
}
|