@tstdl/base 0.93.139 → 0.93.141
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/README.md +166 -0
- package/ai/genkit/multi-region.plugin.js +5 -3
- package/ai/genkit/tests/multi-region.test.d.ts +1 -0
- package/ai/genkit/tests/multi-region.test.js +5 -2
- package/ai/parser/parser.js +2 -2
- package/ai/prompts/build.js +1 -0
- package/ai/prompts/instructions-formatter.d.ts +15 -2
- package/ai/prompts/instructions-formatter.js +36 -31
- package/ai/prompts/prompt-builder.js +5 -5
- package/ai/prompts/steering.d.ts +3 -2
- package/ai/prompts/steering.js +3 -1
- package/ai/tests/instructions-formatter.test.js +1 -0
- package/api/README.md +403 -0
- package/api/client/client.js +7 -13
- package/api/client/tests/api-client.test.js +10 -10
- package/api/default-error-handlers.js +1 -1
- package/api/response.d.ts +2 -2
- package/api/response.js +22 -33
- package/api/server/api-controller.d.ts +1 -1
- package/api/server/api-controller.js +3 -3
- package/api/server/api-request-token.provider.d.ts +1 -0
- package/api/server/api-request-token.provider.js +1 -0
- package/api/server/middlewares/allowed-methods.middleware.js +2 -1
- package/api/server/middlewares/content-type.middleware.js +2 -1
- package/api/types.d.ts +3 -2
- package/application/README.md +240 -0
- package/application/application.d.ts +1 -1
- package/application/application.js +3 -3
- package/application/providers.d.ts +20 -2
- package/application/providers.js +34 -7
- package/audit/README.md +267 -0
- package/audit/module.d.ts +5 -0
- package/audit/module.js +9 -1
- package/authentication/README.md +288 -0
- package/authentication/client/authentication.service.d.ts +12 -11
- package/authentication/client/authentication.service.js +21 -21
- package/authentication/client/http-client.middleware.js +2 -2
- package/authentication/server/module.d.ts +5 -0
- package/authentication/server/module.js +9 -1
- package/authentication/tests/authentication.api-controller.test.js +1 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +1 -1
- package/authentication/tests/authentication.client-error-handling.test.js +2 -1
- package/authentication/tests/authentication.client-service-refresh.test.js +5 -3
- package/authentication/tests/authentication.client-service.test.js +1 -1
- package/browser/README.md +401 -0
- package/cancellation/README.md +156 -0
- package/cancellation/tests/coverage.test.d.ts +1 -0
- package/cancellation/tests/coverage.test.js +49 -0
- package/cancellation/tests/leak.test.js +24 -29
- package/cancellation/tests/token.test.d.ts +1 -0
- package/cancellation/tests/token.test.js +136 -0
- package/cancellation/token.d.ts +53 -177
- package/cancellation/token.js +132 -208
- package/circuit-breaker/postgres/module.d.ts +1 -0
- package/circuit-breaker/postgres/module.js +5 -1
- package/context/README.md +174 -0
- package/cookie/README.md +161 -0
- package/css/README.md +157 -0
- package/data-structures/README.md +320 -0
- package/decorators/README.md +140 -0
- package/distributed-loop/README.md +231 -0
- package/distributed-loop/distributed-loop.js +1 -1
- package/document-management/README.md +403 -0
- package/document-management/server/configure.js +5 -1
- package/document-management/server/module.d.ts +1 -1
- package/document-management/server/module.js +1 -1
- package/document-management/server/services/document-management-ancillary.service.js +1 -1
- package/document-management/server/services/document-management.service.js +9 -7
- package/document-management/tests/ai-config-hierarchy.test.js +0 -5
- package/document-management/tests/document-management-ai-overrides.test.js +0 -1
- package/document-management/tests/document-management-core.test.js +2 -7
- package/document-management/tests/document-management.api.test.js +6 -7
- package/document-management/tests/document-statistics.service.test.js +11 -12
- package/document-management/tests/document-validation-ai-overrides.test.js +0 -1
- package/document-management/tests/document.service.test.js +3 -3
- package/document-management/tests/enum-helpers.test.js +2 -3
- package/dom/README.md +213 -0
- package/enumerable/README.md +259 -0
- package/enumeration/README.md +121 -0
- package/errors/README.md +267 -0
- package/examples/document-management/main.d.ts +1 -0
- package/examples/document-management/main.js +14 -11
- package/file/README.md +191 -0
- package/formats/README.md +210 -0
- package/function/README.md +144 -0
- package/http/README.md +318 -0
- package/http/client/adapters/undici.adapter.js +1 -1
- package/http/client/http-client-request.d.ts +6 -5
- package/http/client/http-client-request.js +8 -9
- package/http/server/node/node-http-server.js +1 -2
- package/image-service/README.md +137 -0
- package/injector/README.md +491 -0
- package/intl/README.md +113 -0
- package/json-path/README.md +182 -0
- package/jsx/README.md +154 -0
- package/key-value-store/README.md +191 -0
- package/key-value-store/postgres/module.d.ts +1 -0
- package/key-value-store/postgres/module.js +5 -1
- package/lock/README.md +249 -0
- package/lock/postgres/module.d.ts +1 -0
- package/lock/postgres/module.js +5 -1
- package/lock/web/web-lock.js +119 -47
- package/logger/README.md +287 -0
- package/mail/README.md +256 -0
- package/mail/module.d.ts +5 -1
- package/mail/module.js +11 -6
- package/memory/README.md +144 -0
- package/message-bus/README.md +244 -0
- package/message-bus/message-bus-base.js +1 -1
- package/module/README.md +182 -0
- package/module/module.d.ts +1 -1
- package/module/module.js +77 -17
- package/module/modules/web-server.module.js +3 -4
- package/notification/server/module.d.ts +1 -0
- package/notification/server/module.js +5 -1
- package/notification/tests/notification-flow.test.js +2 -2
- package/notification/tests/notification-type.service.test.js +24 -15
- package/object-storage/README.md +300 -0
- package/openid-connect/README.md +274 -0
- package/orm/README.md +423 -0
- package/orm/decorators.d.ts +5 -1
- package/orm/decorators.js +1 -1
- package/orm/server/drizzle/schema-converter.js +17 -30
- package/orm/server/encryption.d.ts +0 -1
- package/orm/server/encryption.js +1 -4
- package/orm/server/index.d.ts +1 -6
- package/orm/server/index.js +1 -6
- package/orm/server/migration.d.ts +19 -0
- package/orm/server/migration.js +72 -0
- package/orm/server/repository.d.ts +1 -1
- package/orm/server/transaction.d.ts +5 -10
- package/orm/server/transaction.js +22 -26
- package/orm/server/transactional.js +3 -3
- package/orm/tests/database-migration.test.d.ts +1 -0
- package/orm/tests/database-migration.test.js +82 -0
- package/orm/tests/encryption.test.js +3 -4
- package/orm/utils.d.ts +17 -2
- package/orm/utils.js +49 -1
- package/package.json +9 -6
- package/password/README.md +164 -0
- package/pdf/README.md +246 -0
- package/polyfills.js +1 -0
- package/pool/README.md +198 -0
- package/process/README.md +237 -0
- package/promise/README.md +252 -0
- package/promise/cancelable-promise.js +1 -1
- package/random/README.md +193 -0
- package/rate-limit/postgres/module.d.ts +1 -0
- package/rate-limit/postgres/module.js +5 -1
- package/reflection/README.md +305 -0
- package/reflection/decorator-data.js +11 -12
- package/rpc/README.md +386 -0
- package/rxjs-utils/README.md +262 -0
- package/schema/README.md +342 -0
- package/serializer/README.md +342 -0
- package/signals/implementation/README.md +134 -0
- package/sse/README.md +278 -0
- package/task-queue/README.md +293 -0
- package/task-queue/postgres/drizzle/{0000_simple_invisible_woman.sql → 0000_wakeful_sunspot.sql} +22 -14
- package/task-queue/postgres/drizzle/meta/0000_snapshot.json +160 -82
- package/task-queue/postgres/drizzle/meta/_journal.json +2 -2
- package/task-queue/postgres/module.d.ts +1 -0
- package/task-queue/postgres/module.js +5 -1
- package/task-queue/postgres/schemas.d.ts +9 -6
- package/task-queue/postgres/schemas.js +4 -3
- package/task-queue/postgres/task-queue.d.ts +4 -13
- package/task-queue/postgres/task-queue.js +462 -355
- package/task-queue/postgres/task.model.d.ts +12 -5
- package/task-queue/postgres/task.model.js +51 -25
- package/task-queue/task-context.d.ts +2 -2
- package/task-queue/task-context.js +8 -8
- package/task-queue/task-queue.d.ts +53 -19
- package/task-queue/task-queue.js +121 -55
- package/task-queue/tests/cascading-cancellations.test.d.ts +1 -0
- package/task-queue/tests/cascading-cancellations.test.js +38 -0
- package/task-queue/tests/complex.test.js +45 -229
- package/task-queue/tests/coverage-branch.test.d.ts +1 -0
- package/task-queue/tests/coverage-branch.test.js +407 -0
- package/task-queue/tests/coverage-enhancement.test.d.ts +1 -0
- package/task-queue/tests/coverage-enhancement.test.js +144 -0
- package/task-queue/tests/dag-dependencies.test.d.ts +1 -0
- package/task-queue/tests/dag-dependencies.test.js +41 -0
- package/task-queue/tests/dependencies.test.js +28 -26
- package/task-queue/tests/extensive-dependencies.test.js +64 -139
- package/task-queue/tests/fan-out-spawning.test.d.ts +1 -0
- package/task-queue/tests/fan-out-spawning.test.js +53 -0
- package/task-queue/tests/idempotent-replacement.test.d.ts +1 -0
- package/task-queue/tests/idempotent-replacement.test.js +61 -0
- package/task-queue/tests/missing-idempotent-tasks.test.d.ts +1 -0
- package/task-queue/tests/missing-idempotent-tasks.test.js +38 -0
- package/task-queue/tests/queue.test.js +128 -8
- package/task-queue/tests/worker.test.js +39 -16
- package/task-queue/tests/zombie-parent.test.d.ts +1 -0
- package/task-queue/tests/zombie-parent.test.js +45 -0
- package/task-queue/tests/zombie-recovery.test.d.ts +1 -0
- package/task-queue/tests/zombie-recovery.test.js +51 -0
- package/templates/README.md +287 -0
- package/test5.js +5 -5
- package/testing/README.md +157 -0
- package/testing/integration-setup.d.ts +4 -4
- package/testing/integration-setup.js +54 -29
- package/text/README.md +346 -0
- package/text/localization.service.js +2 -2
- package/threading/README.md +238 -0
- package/types/README.md +311 -0
- package/utils/README.md +322 -0
- package/utils/async-iterable-helpers/observable-iterable.d.ts +1 -1
- package/utils/async-iterable-helpers/observable-iterable.js +4 -8
- package/utils/async-iterable-helpers/take-until.js +4 -4
- package/utils/backoff.js +89 -30
- package/utils/file-reader.js +1 -2
- package/utils/retry-with-backoff.js +1 -1
- package/utils/timer.d.ts +1 -1
- package/utils/timer.js +5 -7
- package/utils/timing.d.ts +1 -1
- package/utils/timing.js +2 -4
- package/utils/z-base32.d.ts +1 -0
- package/utils/z-base32.js +1 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
|
2
|
+
import { provideInitializer } from '../../application/providers.js';
|
|
3
|
+
import { injectionToken, Injector } from '../../injector/index.js';
|
|
4
|
+
import { inject, injectAll, runInInjectionContext } from '../../injector/inject.js';
|
|
5
|
+
import { Logger } from '../../logger/index.js';
|
|
6
|
+
import { isDefined } from '../../utils/type-guards.js';
|
|
7
|
+
import { Database } from './database.js';
|
|
8
|
+
export const DATABASE_MIGRATION = injectionToken('DatabaseMigration');
|
|
9
|
+
/**
|
|
10
|
+
* Registers a database migration in the provided injector.
|
|
11
|
+
* @param name - The unique name of the migration.
|
|
12
|
+
* @param migrate - The migration function.
|
|
13
|
+
* @param options - Optional injector and dependencies.
|
|
14
|
+
*/
|
|
15
|
+
export function registerDatabaseMigration(name, migrate, { injector, dependencies } = {}) {
|
|
16
|
+
const targetInjector = injector ?? Injector;
|
|
17
|
+
targetInjector.register(DATABASE_MIGRATION, { useValue: { name, migrate, dependencies } }, { multi: true });
|
|
18
|
+
}
|
|
19
|
+
export function provideDatabaseMigrator() {
|
|
20
|
+
return provideInitializer(runDatabaseMigrations);
|
|
21
|
+
}
|
|
22
|
+
export async function runDatabaseMigrations() {
|
|
23
|
+
const injector = inject(Injector);
|
|
24
|
+
const database = inject(Database);
|
|
25
|
+
const logger = inject(Logger, 'DatabaseMigrationOrchestrator');
|
|
26
|
+
const migrations = injectAll(DATABASE_MIGRATION, undefined, { optional: true });
|
|
27
|
+
if (migrations.length == 0) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const lockId = 123456789; // Fixed lock ID for migrations
|
|
31
|
+
await database.execute(sql `SELECT pg_advisory_lock(${lockId})`);
|
|
32
|
+
try {
|
|
33
|
+
const sortedMigrations = sortMigrations(migrations);
|
|
34
|
+
for (const migration of sortedMigrations) {
|
|
35
|
+
logger.info(`Running database migration: ${migration.name}`);
|
|
36
|
+
await runInInjectionContext(injector, async () => await migration.migrate());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
await database.execute(sql `SELECT pg_advisory_unlock(${lockId})`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function sortMigrations(migrations) {
|
|
44
|
+
const result = [];
|
|
45
|
+
const visited = new Set();
|
|
46
|
+
const visiting = new Set();
|
|
47
|
+
const migrationMap = new Map(migrations.map((m) => [m.name, m]));
|
|
48
|
+
function visit(name) {
|
|
49
|
+
if (visited.has(name)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (visiting.has(name)) {
|
|
53
|
+
throw new Error(`Circular dependency detected in database migrations: ${name}`);
|
|
54
|
+
}
|
|
55
|
+
visiting.add(name);
|
|
56
|
+
const migration = migrationMap.get(name);
|
|
57
|
+
if (isDefined(migration?.dependencies)) {
|
|
58
|
+
for (const dependency of migration.dependencies) {
|
|
59
|
+
visit(dependency);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
visiting.delete(name);
|
|
63
|
+
visited.add(name);
|
|
64
|
+
if (isDefined(migration)) {
|
|
65
|
+
result.push(migration);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
for (const migration of migrations) {
|
|
69
|
+
visit(migration.name);
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
@@ -18,7 +18,7 @@ type EntityRepositoryContext = {
|
|
|
18
18
|
encryptionSecret: Uint8Array<ArrayBuffer> | undefined;
|
|
19
19
|
transformContext: TransformContext | Promise<TransformContext> | undefined;
|
|
20
20
|
};
|
|
21
|
-
type InferSelect<T extends BaseEntity = BaseEntity> = PgTableFromType<EntityType<T>>['$inferSelect'];
|
|
21
|
+
export type InferSelect<T extends BaseEntity = BaseEntity> = PgTableFromType<EntityType<T>>['$inferSelect'];
|
|
22
22
|
export declare class EntityRepository<T extends BaseEntity = BaseEntity> extends Transactional<EntityRepositoryContext> implements Resolvable<EntityType<T>> {
|
|
23
23
|
#private;
|
|
24
24
|
readonly type: EntityType<T>;
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
/** biome-ignore-all lint/nursery/noExcessiveClassesPerFile: <explanation> */
|
|
1
2
|
import { PgTransaction as DrizzlePgTransaction, type PgQueryResultHKT, type PgTransactionConfig } from 'drizzle-orm/pg-core';
|
|
2
3
|
import type { Record } from '../../types/index.js';
|
|
3
4
|
import type { Database } from './database.js';
|
|
4
5
|
export type PgTransaction = DrizzlePgTransaction<PgQueryResultHKT, Record, Record>;
|
|
5
6
|
export { DrizzlePgTransaction };
|
|
6
7
|
export type TransactionConfig = PgTransactionConfig;
|
|
7
|
-
export declare
|
|
8
|
+
export declare class Transaction implements AsyncDisposable {
|
|
8
9
|
#private;
|
|
10
|
+
readonly pgTransaction: PgTransaction;
|
|
9
11
|
readonly afterCommit: import("../../utils/async-hook/index.js").AsyncHook<never, never, unknown>;
|
|
10
12
|
readonly parent?: Transaction;
|
|
11
13
|
manualCommit: boolean;
|
|
12
|
-
constructor(parent?: Transaction);
|
|
14
|
+
constructor(pgTransaction: PgTransaction, parent?: Transaction);
|
|
15
|
+
static create(session: Database | PgTransaction, config?: TransactionConfig, parent?: Transaction): Promise<Transaction>;
|
|
13
16
|
[Symbol.asyncDispose](): Promise<void>;
|
|
14
17
|
withManualCommit(): void;
|
|
15
18
|
/**
|
|
@@ -19,14 +22,6 @@ export declare abstract class Transaction implements AsyncDisposable {
|
|
|
19
22
|
use<T>(handler: () => Promise<T>): Promise<T>;
|
|
20
23
|
commit(): Promise<void>;
|
|
21
24
|
rollback(): Promise<void>;
|
|
22
|
-
protected abstract _commit(): void | Promise<void>;
|
|
23
|
-
protected abstract _rollback(): void | Promise<void>;
|
|
24
|
-
}
|
|
25
|
-
export declare class DrizzleTransaction extends Transaction {
|
|
26
|
-
#private;
|
|
27
|
-
readonly pgTransaction: PgTransaction;
|
|
28
|
-
constructor(pgTransaction: PgTransaction, parent?: Transaction);
|
|
29
|
-
static create(session: Database | PgTransaction, config?: TransactionConfig, parent?: Transaction): Promise<DrizzleTransaction>;
|
|
30
25
|
protected _commit(): Promise<void>;
|
|
31
26
|
protected _rollback(): Promise<void>;
|
|
32
27
|
private setTransactionResultPromise;
|
|
@@ -1,16 +1,37 @@
|
|
|
1
|
+
/** biome-ignore-all lint/nursery/noExcessiveClassesPerFile: <explanation> */
|
|
1
2
|
import { PgTransaction as DrizzlePgTransaction } from 'drizzle-orm/pg-core';
|
|
2
3
|
import { DeferredPromise } from '../../promise/deferred-promise.js';
|
|
3
4
|
import { asyncHook } from '../../utils/async-hook/index.js';
|
|
4
5
|
export { DrizzlePgTransaction };
|
|
5
6
|
export class Transaction {
|
|
7
|
+
#deferPromise = new DeferredPromise();
|
|
8
|
+
#pgTransactionResultPromise;
|
|
6
9
|
#useCounter = 0;
|
|
7
10
|
#done = false;
|
|
11
|
+
pgTransaction;
|
|
8
12
|
afterCommit = asyncHook();
|
|
9
13
|
parent;
|
|
10
14
|
manualCommit = false;
|
|
11
|
-
constructor(parent) {
|
|
15
|
+
constructor(pgTransaction, parent) {
|
|
16
|
+
this.pgTransaction = pgTransaction;
|
|
12
17
|
this.parent = parent;
|
|
13
18
|
}
|
|
19
|
+
static async create(session, config, parent) {
|
|
20
|
+
const instancePromise = new DeferredPromise();
|
|
21
|
+
const pgTransactionResultPromise = session.transaction(async (tx) => {
|
|
22
|
+
const transaction = new Transaction(tx, parent);
|
|
23
|
+
instancePromise.resolve(transaction);
|
|
24
|
+
await transaction.#deferPromise;
|
|
25
|
+
}, config);
|
|
26
|
+
pgTransactionResultPromise.catch((error) => {
|
|
27
|
+
if (instancePromise.pending) {
|
|
28
|
+
instancePromise.reject(error);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const transaction = await instancePromise;
|
|
32
|
+
transaction.setTransactionResultPromise(pgTransactionResultPromise);
|
|
33
|
+
return transaction;
|
|
34
|
+
}
|
|
14
35
|
async [Symbol.asyncDispose]() {
|
|
15
36
|
if (!this.#done) {
|
|
16
37
|
await this.rollback();
|
|
@@ -67,31 +88,6 @@ export class Transaction {
|
|
|
67
88
|
this.#done = true;
|
|
68
89
|
await this._rollback();
|
|
69
90
|
}
|
|
70
|
-
}
|
|
71
|
-
export class DrizzleTransaction extends Transaction {
|
|
72
|
-
#deferPromise = new DeferredPromise();
|
|
73
|
-
pgTransaction;
|
|
74
|
-
#pgTransactionResultPromise;
|
|
75
|
-
constructor(pgTransaction, parent) {
|
|
76
|
-
super(parent);
|
|
77
|
-
this.pgTransaction = pgTransaction;
|
|
78
|
-
}
|
|
79
|
-
static async create(session, config, parent) {
|
|
80
|
-
const instancePromise = new DeferredPromise();
|
|
81
|
-
const pgTransactionResultPromise = session.transaction(async (tx) => {
|
|
82
|
-
const transaction = new DrizzleTransaction(tx, parent);
|
|
83
|
-
instancePromise.resolve(transaction);
|
|
84
|
-
await transaction.#deferPromise;
|
|
85
|
-
}, config);
|
|
86
|
-
pgTransactionResultPromise.catch((error) => {
|
|
87
|
-
if (instancePromise.pending) {
|
|
88
|
-
instancePromise.reject(error);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
const transaction = await instancePromise;
|
|
92
|
-
transaction.setTransactionResultPromise(pgTransactionResultPromise);
|
|
93
|
-
return transaction;
|
|
94
|
-
}
|
|
95
91
|
async _commit() {
|
|
96
92
|
this.#deferPromise.resolve();
|
|
97
93
|
await this.#pgTransactionResultPromise;
|
|
@@ -4,7 +4,7 @@ import { Injector } from '../../injector/index.js';
|
|
|
4
4
|
import { inject, injectAsync, runInInjectionContext } from '../../injector/inject.js';
|
|
5
5
|
import { isDefined, isNull, isUndefined } from '../../utils/type-guards.js';
|
|
6
6
|
import { Database } from './database.js';
|
|
7
|
-
import {
|
|
7
|
+
import { Transaction } from './transaction.js';
|
|
8
8
|
const transactionCache = new WeakMap();
|
|
9
9
|
const { getCurrentTransactionalContext, isInTransactionalContext, runInTransactionalContext, tryGetCurrentTransactionalContext } = createContextProvider('Transactional');
|
|
10
10
|
export { getCurrentTransactionalContext, isInTransactionalContext, runInTransactionalContext, tryGetCurrentTransactionalContext };
|
|
@@ -58,7 +58,7 @@ export class Transactional {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
const parentTransaction = tryGetTstdlTransaction(this.session);
|
|
61
|
-
const transaction = await
|
|
61
|
+
const transaction = await Transaction.create(this.session, config, parentTransaction);
|
|
62
62
|
transactionCache.set(transaction.pgTransaction, transaction);
|
|
63
63
|
return transaction;
|
|
64
64
|
}
|
|
@@ -77,7 +77,7 @@ export class Transactional {
|
|
|
77
77
|
return this;
|
|
78
78
|
}
|
|
79
79
|
const context = {
|
|
80
|
-
session
|
|
80
|
+
session,
|
|
81
81
|
instances: this.#instances,
|
|
82
82
|
data: this.getTransactionalContextData(),
|
|
83
83
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { APPLICATION_INITIALIZER } from '../../application/application.js';
|
|
2
|
+
import { runInInjectionContext } from '../../injector/inject.js';
|
|
3
|
+
import { DATABASE_MIGRATION, provideDatabaseMigrator, runDatabaseMigrations } from '../../orm/server/migration.js';
|
|
4
|
+
import { setupIntegrationTest } from '../../testing/index.js';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
describe('Database Migration Orchestration', () => {
|
|
7
|
+
it('should have DATABASE_MIGRATION token defined', () => {
|
|
8
|
+
expect(DATABASE_MIGRATION).toBeDefined();
|
|
9
|
+
});
|
|
10
|
+
it('should provide database migrator as application initializer', () => {
|
|
11
|
+
const provider = provideDatabaseMigrator();
|
|
12
|
+
expect(provider.provide).toBe(APPLICATION_INITIALIZER);
|
|
13
|
+
expect(provider.useValue).toBe(runDatabaseMigrations);
|
|
14
|
+
expect(provider.multi).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
it('should register and resolve a migration', async () => {
|
|
17
|
+
const { injector } = await setupIntegrationTest();
|
|
18
|
+
let migrated = false;
|
|
19
|
+
const migration = {
|
|
20
|
+
name: 'test-migration',
|
|
21
|
+
migrate: async () => {
|
|
22
|
+
migrated = true;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
injector.register(DATABASE_MIGRATION, { useValue: migration }, { multi: true });
|
|
26
|
+
const migrations = injector.resolveAll(DATABASE_MIGRATION);
|
|
27
|
+
expect(migrations).toHaveLength(1);
|
|
28
|
+
const firstMigration = migrations[0];
|
|
29
|
+
expect(firstMigration.name).toBe('test-migration');
|
|
30
|
+
await firstMigration.migrate();
|
|
31
|
+
expect(migrated).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('should run migrations in the correct order based on dependencies', async () => {
|
|
34
|
+
const { injector } = await setupIntegrationTest();
|
|
35
|
+
const executionOrder = [];
|
|
36
|
+
const migrationA = {
|
|
37
|
+
name: 'migration-a',
|
|
38
|
+
migrate: async () => {
|
|
39
|
+
executionOrder.push('a');
|
|
40
|
+
},
|
|
41
|
+
dependencies: ['migration-b'],
|
|
42
|
+
};
|
|
43
|
+
const migrationB = {
|
|
44
|
+
name: 'migration-b',
|
|
45
|
+
migrate: async () => {
|
|
46
|
+
executionOrder.push('b');
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
const migrationC = {
|
|
50
|
+
name: 'migration-c',
|
|
51
|
+
migrate: async () => {
|
|
52
|
+
executionOrder.push('c');
|
|
53
|
+
},
|
|
54
|
+
dependencies: ['migration-a'],
|
|
55
|
+
};
|
|
56
|
+
injector.register(DATABASE_MIGRATION, { useValue: migrationA }, { multi: true });
|
|
57
|
+
injector.register(DATABASE_MIGRATION, { useValue: migrationB }, { multi: true });
|
|
58
|
+
injector.register(DATABASE_MIGRATION, { useValue: migrationC }, { multi: true });
|
|
59
|
+
await runInInjectionContext(injector, async () => {
|
|
60
|
+
await runDatabaseMigrations();
|
|
61
|
+
});
|
|
62
|
+
expect(executionOrder).toEqual(['b', 'a', 'c']);
|
|
63
|
+
});
|
|
64
|
+
it('should throw on circular dependency', async () => {
|
|
65
|
+
const { injector } = await setupIntegrationTest();
|
|
66
|
+
const migrationA = {
|
|
67
|
+
name: 'migration-a',
|
|
68
|
+
migrate: async () => { },
|
|
69
|
+
dependencies: ['migration-b'],
|
|
70
|
+
};
|
|
71
|
+
const migrationB = {
|
|
72
|
+
name: 'migration-b',
|
|
73
|
+
migrate: async () => { },
|
|
74
|
+
dependencies: ['migration-a'],
|
|
75
|
+
};
|
|
76
|
+
injector.register(DATABASE_MIGRATION, { useValue: migrationA }, { multi: true });
|
|
77
|
+
injector.register(DATABASE_MIGRATION, { useValue: migrationB }, { multi: true });
|
|
78
|
+
await runInInjectionContext(injector, async () => {
|
|
79
|
+
await expect(runDatabaseMigrations()).rejects.toThrow('Circular dependency detected');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DetailsError } from '../../errors/index.js';
|
|
2
1
|
import { describe, expect, test } from 'vitest';
|
|
3
2
|
import { decryptBytes, encryptBytes } from '../server/encryption.js';
|
|
4
3
|
describe('ORM Encryption', () => {
|
|
@@ -12,15 +11,15 @@ describe('ORM Encryption', () => {
|
|
|
12
11
|
const decrypted = await decryptBytes(encrypted, key);
|
|
13
12
|
expect(new TextDecoder().decode(decrypted)).toBe('Hello, ORM Encryption!');
|
|
14
13
|
});
|
|
15
|
-
test('should throw
|
|
14
|
+
test('should throw Error on corrupted data', async () => {
|
|
16
15
|
const key = await generateKey();
|
|
17
16
|
const data = new TextEncoder().encode('Corrupt me');
|
|
18
17
|
const encrypted = await encryptBytes(data, key);
|
|
19
18
|
// Corrupt the ciphertext (last byte)
|
|
20
19
|
const lastIndex = encrypted.length - 1;
|
|
21
20
|
encrypted[lastIndex] = encrypted[lastIndex] ^ 1;
|
|
22
|
-
await expect(decryptBytes(encrypted, key)).rejects.toThrow(
|
|
23
|
-
await expect(decryptBytes(encrypted, key)).rejects.toThrow('
|
|
21
|
+
await expect(decryptBytes(encrypted, key)).rejects.toThrow(Error);
|
|
22
|
+
await expect(decryptBytes(encrypted, key)).rejects.toThrow('Decryption failed.');
|
|
24
23
|
});
|
|
25
24
|
test('should throw error on invalid version', async () => {
|
|
26
25
|
const key = await generateKey();
|
package/orm/utils.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { type PropertyMetadata } from '../reflection/registry.js';
|
|
6
6
|
import type { AbstractConstructor } from '../types/index.js';
|
|
7
|
-
import type { InheritanceMetadata } from './decorators.js';
|
|
8
|
-
import type { BaseEntity, Entity } from './entity.js';
|
|
7
|
+
import type { InheritanceMetadata, OrmTableReflectionData } from './decorators.js';
|
|
8
|
+
import type { BaseEntity, Entity, EntityType } from './entity.js';
|
|
9
9
|
/**
|
|
10
10
|
* Converts an array of entities into a Map keyed by entity ID.
|
|
11
11
|
* @template T - The entity type, must extend `Entity` (i.e., have an `id` property).
|
|
@@ -24,3 +24,18 @@ export declare function getEntityIds(entities: (Entity | BaseEntity)[]): string[
|
|
|
24
24
|
export declare function isChildEntity(type: AbstractConstructor): boolean;
|
|
25
25
|
export declare function getInheritanceMetadata(type: AbstractConstructor): InheritanceMetadata | undefined;
|
|
26
26
|
export declare function getOwnProperties(type: AbstractConstructor): PropertyMetadata[];
|
|
27
|
+
/**
|
|
28
|
+
* Gets the database schema name for a given entity type, traversing the inheritance hierarchy.
|
|
29
|
+
* @param type The entity class.
|
|
30
|
+
* @param fallback Optional fallback schema name if none is defined in metadata.
|
|
31
|
+
* @returns The resolved schema name.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getEntitySchema(type: AbstractConstructor, fallback?: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Gets the database table name for a given entity type, traversing the inheritance hierarchy.
|
|
36
|
+
* @param type The entity class.
|
|
37
|
+
* @returns The resolved table name.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getEntityTableName(type: AbstractConstructor): string;
|
|
40
|
+
export declare function getDefaultTableName(type: EntityType): string;
|
|
41
|
+
export declare function getTableReflectionDatas(type: EntityType): OrmTableReflectionData[];
|
package/orm/utils.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Provides utility functions for working with ORM entities.
|
|
4
4
|
*/
|
|
5
5
|
import { reflectionRegistry } from '../reflection/registry.js';
|
|
6
|
-
import {
|
|
6
|
+
import { toSnakeCase } from '../utils/string/index.js';
|
|
7
|
+
import { assertDefined, isDefined, isNotNullOrUndefined, isString, isUndefined } from '../utils/type-guards.js';
|
|
7
8
|
export function getEntityMap(entities, selector = (entity) => entity.id) {
|
|
8
9
|
const entries = entities.map((entity) => [selector(entity), entity]);
|
|
9
10
|
return new Map(entries);
|
|
@@ -36,3 +37,50 @@ export function getOwnProperties(type) {
|
|
|
36
37
|
}
|
|
37
38
|
return [...metadata.properties.values()].filter((property) => !property.inherited || (property.key == 'id'));
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Gets the database schema name for a given entity type, traversing the inheritance hierarchy.
|
|
42
|
+
* @param type The entity class.
|
|
43
|
+
* @param fallback Optional fallback schema name if none is defined in metadata.
|
|
44
|
+
* @returns The resolved schema name.
|
|
45
|
+
*/
|
|
46
|
+
export function getEntitySchema(type, fallback) {
|
|
47
|
+
for (let currentMetadata = reflectionRegistry.getMetadata(type); isNotNullOrUndefined(currentMetadata); currentMetadata = isNotNullOrUndefined(currentMetadata.parent) ? reflectionRegistry.getMetadata(currentMetadata.parent) : undefined) {
|
|
48
|
+
const schema = currentMetadata.data.tryGet('orm')?.schema;
|
|
49
|
+
if (isDefined(schema)) {
|
|
50
|
+
return schema;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (isDefined(fallback)) {
|
|
54
|
+
return fallback;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Schema not found for entity ${type.name} and no fallback provided.`);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Gets the database table name for a given entity type, traversing the inheritance hierarchy.
|
|
60
|
+
* @param type The entity class.
|
|
61
|
+
* @returns The resolved table name.
|
|
62
|
+
*/
|
|
63
|
+
export function getEntityTableName(type) {
|
|
64
|
+
for (let currentMetadata = reflectionRegistry.getMetadata(type); isNotNullOrUndefined(currentMetadata); currentMetadata = isNotNullOrUndefined(currentMetadata.parent) ? reflectionRegistry.getMetadata(currentMetadata.parent) : undefined) {
|
|
65
|
+
const name = currentMetadata.data.tryGet('orm')?.name;
|
|
66
|
+
if (isDefined(name)) {
|
|
67
|
+
return name;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return getDefaultTableName(type);
|
|
71
|
+
}
|
|
72
|
+
export function getDefaultTableName(type) {
|
|
73
|
+
return toSnakeCase(isString(type.entityName) ? type.entityName : type.name.replace(/\d+$/u, ''));
|
|
74
|
+
}
|
|
75
|
+
export function getTableReflectionDatas(type) {
|
|
76
|
+
const metadata = reflectionRegistry.getMetadata(type);
|
|
77
|
+
assertDefined(metadata, `Type ${type.name} does not have reflection metadata.`);
|
|
78
|
+
const tableReflectionDatas = [];
|
|
79
|
+
for (let currentMetadata = metadata; isNotNullOrUndefined(currentMetadata?.parent); currentMetadata = reflectionRegistry.getMetadata(currentMetadata.parent)) {
|
|
80
|
+
const tableReflectionData = currentMetadata.data.tryGet('orm');
|
|
81
|
+
if (isDefined(tableReflectionData)) {
|
|
82
|
+
tableReflectionDatas.push(tableReflectionData);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return tableReflectionDatas;
|
|
86
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.93.
|
|
3
|
+
"version": "0.93.141",
|
|
4
4
|
"author": "Patrick Hein",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -10,16 +10,18 @@
|
|
|
10
10
|
"build": "tsc && tsc-alias && npm run copy:orm",
|
|
11
11
|
"build:watch": "concurrently --raw --kill-others npm:tsc:watch npm:tsc-alias:watch",
|
|
12
12
|
"build:production": "rm -rf dist && npm run build && npm run build:production:copy-files",
|
|
13
|
-
"build:production:copy-files": "cp package.json eslint.config.js tsconfig.server.json dist/ && cp tsconfig.base.json dist/tsconfig.json && npm run copy:orm",
|
|
13
|
+
"build:production:copy-files": "cp package.json eslint.config.js tsconfig.server.json dist/ && cp tsconfig.base.json dist/tsconfig.json && npm run copy:orm && npm run copy:readmes",
|
|
14
14
|
"build:dts": "rm -rf dist && tsc -p tsconfig.dts.json && tsc-alias",
|
|
15
15
|
"build:docs": "typedoc",
|
|
16
16
|
"build:docs:watch": "typedoc --watch",
|
|
17
17
|
"lint": "eslint --cache source/",
|
|
18
18
|
"pub": "npm run build:production && npm run cleanup:dist && npm publish dist/",
|
|
19
19
|
"test": "vitest run",
|
|
20
|
+
"test:coverage": "f() { vitest run --coverage --coverage.include=\"source/$1/**/*.ts\" source/$1; }; f",
|
|
20
21
|
"tsc:watch": "tsc --watch",
|
|
21
22
|
"tsc-alias:watch": "tsc-alias --watch",
|
|
22
23
|
"cleanup:dist": "rm -vrf dist/tools/",
|
|
24
|
+
"copy:readmes": "cp README.md dist/ && cd source && find . -name \"README.md\" -exec cp --parents {} ../dist/ \\;",
|
|
23
25
|
"generate:migration": "./scripts/manage-orm.sh generate && npm run copy:orm",
|
|
24
26
|
"generate:readmes": "deno run --allow-run=code2prompt --allow-read --allow-write=source --allow-net=generativelanguage.googleapis.com --allow-env=GEMINI_API_KEY generate-readmes.ts",
|
|
25
27
|
"generate:readmes:new-only": "npm run generate:readmes -- --skip-existing",
|
|
@@ -150,7 +152,9 @@
|
|
|
150
152
|
"type-fest": "^5.4"
|
|
151
153
|
},
|
|
152
154
|
"peerDependencies": {
|
|
153
|
-
"@
|
|
155
|
+
"@aws-sdk/client-s3": "^3.996",
|
|
156
|
+
"@aws-sdk/s3-request-presigner": "^3.996",
|
|
157
|
+
"@genkit-ai/google-genai": "^1.29",
|
|
154
158
|
"@google-cloud/storage": "^7.19",
|
|
155
159
|
"@toon-format/toon": "^2.1.0",
|
|
156
160
|
"@tstdl/angular": "^0.93",
|
|
@@ -160,10 +164,8 @@
|
|
|
160
164
|
"@zxcvbn-ts/language-en": "^3.0",
|
|
161
165
|
"drizzle-orm": "^0.45",
|
|
162
166
|
"file-type": "^21.3",
|
|
163
|
-
"genkit": "^1.
|
|
167
|
+
"genkit": "^1.29",
|
|
164
168
|
"handlebars": "^4.7",
|
|
165
|
-
"@aws-sdk/client-s3": "^3.993",
|
|
166
|
-
"@aws-sdk/s3-request-presigner": "^3.993",
|
|
167
169
|
"mjml": "^4.18",
|
|
168
170
|
"nodemailer": "^8.0",
|
|
169
171
|
"pg": "^8.18",
|
|
@@ -181,6 +183,7 @@
|
|
|
181
183
|
}
|
|
182
184
|
},
|
|
183
185
|
"devDependencies": {
|
|
186
|
+
"@biomejs/biome": "2.4",
|
|
184
187
|
"@stylistic/eslint-plugin": "5.9",
|
|
185
188
|
"@types/koa__router": "12.0",
|
|
186
189
|
"@types/luxon": "3.7",
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# @tstdl/base/password
|
|
2
|
+
|
|
3
|
+
A robust password strength estimation module that combines the advanced heuristic analysis of `zxcvbn-ts` with secure checks against the "Have I Been Pwned" breach database.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [✨ Features](#-features)
|
|
8
|
+
- [Core Concepts](#core-concepts)
|
|
9
|
+
- [Strength Analysis](#strength-analysis-zxcvbn)
|
|
10
|
+
- [Breach Check](#breach-check-hibp)
|
|
11
|
+
- [🚀 Basic Usage](#-basic-usage)
|
|
12
|
+
- [🔧 Advanced Topics](#-advanced-topics)
|
|
13
|
+
- [Localization](#localization)
|
|
14
|
+
- [Offline Checks](#offline-checks)
|
|
15
|
+
- [Schema Integration](#schema-integration)
|
|
16
|
+
- [📚 API](#-api)
|
|
17
|
+
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
- **Advanced Heuristics:** Utilizes `zxcvbn-ts` to detect common patterns, dictionary words, names, keyboard sequences, and dates.
|
|
21
|
+
- **Breach Detection:** Securely checks if a password has appeared in known data breaches via the "Have I Been Pwned" (HIBP) API.
|
|
22
|
+
- **Secure by Design:** Uses k-Anonymity for HIBP checks; the full password hash is never sent over the network.
|
|
23
|
+
- **Unified Scoring:** Provides a consolidated strength score from 0 (Very Weak) to 4 (Very Strong).
|
|
24
|
+
- **Localized Feedback:** Returns localization keys for warnings and suggestions, supporting English and German out of the box.
|
|
25
|
+
- **Schema & Validation Ready:** `PasswordCheckResult` is fully decorated with `@tstdl/base/schema`, making it ready for API contracts and data models.
|
|
26
|
+
- **Performance Optimized:** Dynamically imports heavy analysis libraries only when needed to keep initial bundle sizes low.
|
|
27
|
+
|
|
28
|
+
## Core Concepts
|
|
29
|
+
|
|
30
|
+
### Strength Analysis (zxcvbn)
|
|
31
|
+
|
|
32
|
+
The module uses `zxcvbn-ts` to calculate entropy and estimate crack time. It looks for:
|
|
33
|
+
|
|
34
|
+
- **Dictionaries:** Common passwords, names, and words in multiple languages.
|
|
35
|
+
- **Patterns:** L33t speak, keyboard walks (e.g., "qwerty"), and repetitions.
|
|
36
|
+
- **Personal Info:** Dates and years.
|
|
37
|
+
|
|
38
|
+
### Breach Check (HIBP)
|
|
39
|
+
|
|
40
|
+
To protect against credential stuffing, the module checks the "Have I Been Pwned" database.
|
|
41
|
+
|
|
42
|
+
1. The password is hashed using SHA-1.
|
|
43
|
+
2. Only the first 5 characters of the hash are sent to the API (k-Anonymity).
|
|
44
|
+
3. The API returns all suffixes matching that prefix.
|
|
45
|
+
4. The module checks locally if the full hash exists in the response.
|
|
46
|
+
|
|
47
|
+
If a password is found in the breach database, its strength score is automatically downgraded to **0 (Very Weak)**, regardless of its complexity.
|
|
48
|
+
|
|
49
|
+
## 🚀 Basic Usage
|
|
50
|
+
|
|
51
|
+
The primary entry point is the `checkPassword` function. It returns a `PasswordCheckResult` containing the score, breach count, and feedback keys.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { checkPassword, PasswordStrength } from '@tstdl/base/password';
|
|
55
|
+
|
|
56
|
+
async function validateUserPassword(password: string) {
|
|
57
|
+
const result = await checkPassword(password);
|
|
58
|
+
|
|
59
|
+
console.log(`Score: ${result.strength} (${PasswordStrength[result.strength]})`);
|
|
60
|
+
|
|
61
|
+
if (result.pwned && result.pwned > 0) {
|
|
62
|
+
console.warn(`⚠️ This password has appeared in ${result.pwned} data breaches!`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (result.strength < PasswordStrength.Strong) {
|
|
66
|
+
console.log('Password is too weak.');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
validateUserPassword('correct-horse-battery-staple');
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 🔧 Advanced Topics
|
|
74
|
+
|
|
75
|
+
### Localization
|
|
76
|
+
|
|
77
|
+
The `warnings` and `suggestions` arrays in the result contain **localization keys** (e.g., `tstdl.passwordCheck.warnings.simpleRepeat`) rather than raw text. This allows you to easily translate feedback using the `@tstdl/base/text` module.
|
|
78
|
+
|
|
79
|
+
The module exports `englishPasswordCheckLocalization` and `germanPasswordCheckLocalization`.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { checkPassword, englishPasswordCheckLocalization } from '@tstdl/base/password';
|
|
83
|
+
import { LocalizationService } from '@tstdl/base/text';
|
|
84
|
+
|
|
85
|
+
// 1. Setup the localization service with the provided language pack
|
|
86
|
+
const localization = new LocalizationService([englishPasswordCheckLocalization]);
|
|
87
|
+
localization.setLanguage('en');
|
|
88
|
+
|
|
89
|
+
async function showFeedback(password: string) {
|
|
90
|
+
const result = await checkPassword(password);
|
|
91
|
+
|
|
92
|
+
// 2. Translate the keys to human-readable text
|
|
93
|
+
const warnings = result.warnings.map((key) => localization.localize(key));
|
|
94
|
+
const suggestions = result.suggestions.map((key) => localization.localize(key));
|
|
95
|
+
|
|
96
|
+
console.log('Warnings:', warnings);
|
|
97
|
+
console.log('Suggestions:', suggestions);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Offline Checks
|
|
102
|
+
|
|
103
|
+
If you are in an environment without internet access, or if you want to skip the HTTP request to "Have I Been Pwned" for performance reasons, you can disable it via options.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { checkPassword } from '@tstdl/base/password';
|
|
107
|
+
|
|
108
|
+
async function checkOffline(password: string) {
|
|
109
|
+
// Disables the HIBP API call
|
|
110
|
+
const result = await checkPassword(password, { checkForPwned: false });
|
|
111
|
+
|
|
112
|
+
// result.pwned will be undefined
|
|
113
|
+
console.log(`Local Strength Score: ${result.strength}`);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Schema Integration
|
|
118
|
+
|
|
119
|
+
The `PasswordCheckResult` is a class decorated with `@tstdl/base/schema`. This makes it easy to use as a return type in your API definitions or as a nested property in other models.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { PasswordCheckResult } from '@tstdl/base/password';
|
|
123
|
+
import { defineApi } from '@tstdl/base/api';
|
|
124
|
+
|
|
125
|
+
export const myApiDefinition = defineApi({
|
|
126
|
+
endpoints: {
|
|
127
|
+
checkStrength: {
|
|
128
|
+
method: 'GET',
|
|
129
|
+
parameters: { password: String },
|
|
130
|
+
result: PasswordCheckResult,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Authentication Integration
|
|
137
|
+
|
|
138
|
+
This module is used by the `@tstdl/base/authentication` framework to enforce password requirements during registration or password changes. See `AuthenticationSecretRequirementsValidator` for more details on how to customize these checks in your application.
|
|
139
|
+
|
|
140
|
+
## 📚 API
|
|
141
|
+
|
|
142
|
+
### Functions
|
|
143
|
+
|
|
144
|
+
| Function | Signature | Description |
|
|
145
|
+
| :--------------- | :----------------------------------------------------------------------------------- | :------------------------------------------------------------------------------ |
|
|
146
|
+
| `checkPassword` | `(password: string, options?: CheckPasswordOptions) => Promise<PasswordCheckResult>` | Analyzes password strength and checks for breaches. |
|
|
147
|
+
| `haveIBeenPwned` | `(password: string) => Promise<number>` | Checks the password against the HIBP database and returns the occurrence count. |
|
|
148
|
+
|
|
149
|
+
### Classes & Interfaces
|
|
150
|
+
|
|
151
|
+
| Name | Type | Description |
|
|
152
|
+
| :-------------------------- | :------ | :----------------------------------------------------------------------------------------- |
|
|
153
|
+
| `PasswordCheckResult` | `class` | The result object containing `strength`, `pwned` count, `warnings`, and `suggestions`. |
|
|
154
|
+
| `CheckPasswordOptions` | `type` | Options object. Property `checkForPwned` (boolean, default `true`) controls the API check. |
|
|
155
|
+
| `PasswordCheckLocalization` | `type` | Type definition for the localization structure used by this module. |
|
|
156
|
+
|
|
157
|
+
### Enums & Constants
|
|
158
|
+
|
|
159
|
+
| Name | Type | Description |
|
|
160
|
+
| :--------------------------------- | :------ | :------------------------------------------------------------------------ |
|
|
161
|
+
| `PasswordStrength` | `enum` | `VeryWeak` (0), `Weak` (1), `Medium` (2), `Strong` (3), `VeryStrong` (4). |
|
|
162
|
+
| `englishPasswordCheckLocalization` | `const` | English localization definitions for warnings and suggestions. |
|
|
163
|
+
| `germanPasswordCheckLocalization` | `const` | German localization definitions for warnings and suggestions. |
|
|
164
|
+
| `passwordCheckLocalizationKeys` | `const` | Helper object containing the localization key paths. |
|