@astram/guard 0.1.0

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -0
  3. package/dist/authentication/argon2id-password-hasher.d.ts +61 -0
  4. package/dist/authentication/argon2id-password-hasher.d.ts.map +1 -0
  5. package/dist/authentication/argon2id-password-hasher.js +108 -0
  6. package/dist/boot/boot-sequence.d.ts +43 -0
  7. package/dist/boot/boot-sequence.d.ts.map +1 -0
  8. package/dist/boot/boot-sequence.js +90 -0
  9. package/dist/boot/boot.module.d.ts +8 -0
  10. package/dist/boot/boot.module.d.ts.map +1 -0
  11. package/dist/boot/boot.module.js +24 -0
  12. package/dist/config/app-config.d.ts +112 -0
  13. package/dist/config/app-config.d.ts.map +1 -0
  14. package/dist/config/app-config.js +94 -0
  15. package/dist/config/config.module.d.ts +12 -0
  16. package/dist/config/config.module.d.ts.map +1 -0
  17. package/dist/config/config.module.js +35 -0
  18. package/dist/enforcement/classification.decorator.d.ts +41 -0
  19. package/dist/enforcement/classification.decorator.d.ts.map +1 -0
  20. package/dist/enforcement/classification.decorator.js +46 -0
  21. package/dist/enforcement/core-seam.d.ts +40 -0
  22. package/dist/enforcement/core-seam.d.ts.map +1 -0
  23. package/dist/enforcement/core-seam.js +38 -0
  24. package/dist/enforcement/enforcement.module.d.ts +18 -0
  25. package/dist/enforcement/enforcement.module.d.ts.map +1 -0
  26. package/dist/enforcement/enforcement.module.js +40 -0
  27. package/dist/enforcement/global-enforcement.guard.d.ts +31 -0
  28. package/dist/enforcement/global-enforcement.guard.d.ts.map +1 -0
  29. package/dist/enforcement/global-enforcement.guard.js +76 -0
  30. package/dist/index.d.ts +26 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +88 -0
  33. package/dist/module/shield.module.d.ts +79 -0
  34. package/dist/module/shield.module.d.ts.map +1 -0
  35. package/dist/module/shield.module.js +108 -0
  36. package/dist/session/crypto-adapters.d.ts +60 -0
  37. package/dist/session/crypto-adapters.d.ts.map +1 -0
  38. package/dist/session/crypto-adapters.js +80 -0
  39. package/dist/session/index.d.ts +17 -0
  40. package/dist/session/index.d.ts.map +1 -0
  41. package/dist/session/index.js +32 -0
  42. package/dist/session/mariadb-session-record-store.d.ts +50 -0
  43. package/dist/session/mariadb-session-record-store.d.ts.map +1 -0
  44. package/dist/session/mariadb-session-record-store.js +162 -0
  45. package/dist/session/redis-hot-session-store.d.ts +30 -0
  46. package/dist/session/redis-hot-session-store.d.ts.map +1 -0
  47. package/dist/session/redis-hot-session-store.js +102 -0
  48. package/package.json +52 -0
@@ -0,0 +1,79 @@
1
+ import { type DynamicModule } from '@nestjs/common';
2
+ import type { CoreDecisionAuthority } from '../enforcement/core-seam.js';
3
+ import { type AppConfig } from '../config/app-config.js';
4
+ /**
5
+ * DI token for the Core decision authority (the ADR-0001 single decision
6
+ * authority) that the enforcement guard consults. The consumer supplies the real
7
+ * `@astram/core` authority through {@link ShieldModule.forRoot}; when omitted the
8
+ * framework binds the fail-closed {@link stubDecisionAuthority} (deny-by-default),
9
+ * so a mis-wiring cannot silently open a door.
10
+ */
11
+ export declare const SHIELD_DECISION_AUTHORITY: unique symbol;
12
+ /**
13
+ * Options for {@link ShieldModule.forRoot}. Intentionally minimal (ADR-0023 §7 —
14
+ * no speculative abstraction): the consumer declares Shield through this ONE seam.
15
+ */
16
+ export interface ShieldModuleOptions {
17
+ /**
18
+ * The Core decision authority (`@astram/core`'s `DecisionAuthority`) the global
19
+ * guard invokes for classified routes. Optional in this layer: omit it and the
20
+ * framework binds the fail-closed stub. A later Authorization layer makes this
21
+ * the real Core authority — the guard's call site does not change.
22
+ */
23
+ readonly decisionAuthority?: CoreDecisionAuthority;
24
+ /**
25
+ * Application config (port/environment/ADR-0018 pool budget). Omit to load from
26
+ * the environment via {@link loadAppConfig}. Supplied here so a consumer can
27
+ * inject computed config through the one seam rather than editing the framework.
28
+ */
29
+ readonly config?: AppConfig;
30
+ }
31
+ /** Async variant of {@link ShieldModuleOptions} (DI-computed config). */
32
+ export interface ShieldModuleAsyncOptions {
33
+ /** Providers this factory injects (resolved by the host DI container). */
34
+ readonly inject?: readonly unknown[];
35
+ /** Factory returning {@link ShieldModuleOptions} (may be async). */
36
+ readonly useFactory: (...args: readonly unknown[]) => Promise<ShieldModuleOptions> | ShieldModuleOptions;
37
+ /** Modules exporting the injected providers. */
38
+ readonly imports?: readonly unknown[];
39
+ }
40
+ /**
41
+ * `ShieldModule` — the SINGLE registration seam (ADR-0023 §4).
42
+ *
43
+ * A consumer app declares Shield by importing `ShieldModule.forRoot({...})` ONCE
44
+ * in its root module. The framework — not the consumer — owns composition, and
45
+ * this is the SINGLE wiring path: `ShieldModule` COMPOSES the framework's internal
46
+ * modules via `imports` rather than re-declaring their providers, so each binding
47
+ * has exactly ONE definition (ADR-0023 §4 — one seam, no divergent path):
48
+ *
49
+ * - {@link EnforcementModule} — the SOLE definition of the `APP_GUARD` →
50
+ * {@link GlobalEnforcementGuard} binding (the global, deny-by-default
51
+ * enforcement point, ADR-0002; covers EVERY route). `ShieldModule` never
52
+ * re-binds `APP_GUARD` itself — it imports this module.
53
+ * - {@link BootModule} — the {@link BootSequence} lifecycle hook, which runs the
54
+ * ADR-0018 connection-pool budget assertion at boot (fail-closed).
55
+ * - `APP_CONFIG` + `SHIELD_DECISION_AUTHORITY` — provided by `ShieldModule` here
56
+ * so the ONE seam can honor caller-supplied `config`/`decisionAuthority`
57
+ * (`ConfigModule`'s env-only `APP_CONFIG` is NOT composed, to avoid a second
58
+ * provider for the same token). `SHIELD_DECISION_AUTHORITY` (the ADR-0001 single
59
+ * decision authority) is the real `@astram/core` authority when supplied, else
60
+ * the fail-closed stub.
61
+ *
62
+ * `@Global()` so its exported tokens are visible app-wide without re-importing.
63
+ */
64
+ export declare class ShieldModule {
65
+ /** Register Shield with static options (the common path). */
66
+ static forRoot(options?: ShieldModuleOptions): DynamicModule;
67
+ /**
68
+ * Register Shield with DI-computed options (`forRootAsync`).
69
+ *
70
+ * STUB (ADR-0023): the async factory resolves {@link ShieldModuleOptions} at
71
+ * startup, then composes exactly as {@link forRoot} — same imported
72
+ * {@link EnforcementModule} (the ONE `APP_GUARD` binding) + {@link BootModule}.
73
+ * Only `APP_CONFIG`/`SHIELD_DECISION_AUTHORITY` are provided asynchronously; the
74
+ * guard/boot wiring is identical, so the fail-closed enforcement seam is
75
+ * unchanged whether config is static or computed.
76
+ */
77
+ static forRootAsync(options: ShieldModuleAsyncOptions): DynamicModule;
78
+ }
79
+ //# sourceMappingURL=shield.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shield.module.d.ts","sourceRoot":"","sources":["../../src/module/shield.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAiB,MAAM,gBAAgB,CAAC;AACnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAKzE,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAExE;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,eAAsC,CAAC;AAE7E;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,qBAAqB,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED,yEAAyE;AACzE,MAAM,WAAW,wBAAwB;IACvC,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,CACnB,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,KACxB,OAAO,CAAC,mBAAmB,CAAC,GAAG,mBAAmB,CAAC;IACxD,gDAAgD;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAEa,YAAY;IACvB,6DAA6D;IAC7D,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,mBAAwB,GAAG,aAAa;IAkBhE;;;;;;;;;OASG;IACH,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa;CA8BtE"}
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var ShieldModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ShieldModule = exports.SHIELD_DECISION_AUTHORITY = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const core_seam_js_1 = require("../enforcement/core-seam.js");
13
+ const enforcement_module_js_1 = require("../enforcement/enforcement.module.js");
14
+ const boot_module_js_1 = require("../boot/boot.module.js");
15
+ const config_module_js_1 = require("../config/config.module.js");
16
+ const app_config_js_1 = require("../config/app-config.js");
17
+ /**
18
+ * DI token for the Core decision authority (the ADR-0001 single decision
19
+ * authority) that the enforcement guard consults. The consumer supplies the real
20
+ * `@astram/core` authority through {@link ShieldModule.forRoot}; when omitted the
21
+ * framework binds the fail-closed {@link stubDecisionAuthority} (deny-by-default),
22
+ * so a mis-wiring cannot silently open a door.
23
+ */
24
+ exports.SHIELD_DECISION_AUTHORITY = Symbol('SHIELD_DECISION_AUTHORITY');
25
+ /**
26
+ * `ShieldModule` — the SINGLE registration seam (ADR-0023 §4).
27
+ *
28
+ * A consumer app declares Shield by importing `ShieldModule.forRoot({...})` ONCE
29
+ * in its root module. The framework — not the consumer — owns composition, and
30
+ * this is the SINGLE wiring path: `ShieldModule` COMPOSES the framework's internal
31
+ * modules via `imports` rather than re-declaring their providers, so each binding
32
+ * has exactly ONE definition (ADR-0023 §4 — one seam, no divergent path):
33
+ *
34
+ * - {@link EnforcementModule} — the SOLE definition of the `APP_GUARD` →
35
+ * {@link GlobalEnforcementGuard} binding (the global, deny-by-default
36
+ * enforcement point, ADR-0002; covers EVERY route). `ShieldModule` never
37
+ * re-binds `APP_GUARD` itself — it imports this module.
38
+ * - {@link BootModule} — the {@link BootSequence} lifecycle hook, which runs the
39
+ * ADR-0018 connection-pool budget assertion at boot (fail-closed).
40
+ * - `APP_CONFIG` + `SHIELD_DECISION_AUTHORITY` — provided by `ShieldModule` here
41
+ * so the ONE seam can honor caller-supplied `config`/`decisionAuthority`
42
+ * (`ConfigModule`'s env-only `APP_CONFIG` is NOT composed, to avoid a second
43
+ * provider for the same token). `SHIELD_DECISION_AUTHORITY` (the ADR-0001 single
44
+ * decision authority) is the real `@astram/core` authority when supplied, else
45
+ * the fail-closed stub.
46
+ *
47
+ * `@Global()` so its exported tokens are visible app-wide without re-importing.
48
+ */
49
+ let ShieldModule = ShieldModule_1 = class ShieldModule {
50
+ /** Register Shield with static options (the common path). */
51
+ static forRoot(options = {}) {
52
+ const config = options.config ?? (0, app_config_js_1.loadAppConfig)();
53
+ return {
54
+ module: ShieldModule_1,
55
+ // Compose the framework modules — EnforcementModule holds the ONE APP_GUARD
56
+ // binding; BootModule holds the boot-sequence hook. No re-declaration here.
57
+ imports: [enforcement_module_js_1.EnforcementModule, boot_module_js_1.BootModule],
58
+ providers: [
59
+ { provide: config_module_js_1.APP_CONFIG, useValue: config },
60
+ {
61
+ provide: exports.SHIELD_DECISION_AUTHORITY,
62
+ useValue: options.decisionAuthority ?? core_seam_js_1.stubDecisionAuthority,
63
+ },
64
+ ],
65
+ exports: [config_module_js_1.APP_CONFIG, exports.SHIELD_DECISION_AUTHORITY],
66
+ };
67
+ }
68
+ /**
69
+ * Register Shield with DI-computed options (`forRootAsync`).
70
+ *
71
+ * STUB (ADR-0023): the async factory resolves {@link ShieldModuleOptions} at
72
+ * startup, then composes exactly as {@link forRoot} — same imported
73
+ * {@link EnforcementModule} (the ONE `APP_GUARD` binding) + {@link BootModule}.
74
+ * Only `APP_CONFIG`/`SHIELD_DECISION_AUTHORITY` are provided asynchronously; the
75
+ * guard/boot wiring is identical, so the fail-closed enforcement seam is
76
+ * unchanged whether config is static or computed.
77
+ */
78
+ static forRootAsync(options) {
79
+ const optionsToken = Symbol('SHIELD_MODULE_OPTIONS');
80
+ const asyncOptionsProvider = {
81
+ provide: optionsToken,
82
+ useFactory: options.useFactory,
83
+ inject: (options.inject ?? []),
84
+ };
85
+ const configProvider = {
86
+ provide: config_module_js_1.APP_CONFIG,
87
+ useFactory: (opts) => opts.config ?? (0, app_config_js_1.loadAppConfig)(),
88
+ inject: [optionsToken],
89
+ };
90
+ const authorityProvider = {
91
+ provide: exports.SHIELD_DECISION_AUTHORITY,
92
+ useFactory: (opts) => opts.decisionAuthority ?? core_seam_js_1.stubDecisionAuthority,
93
+ inject: [optionsToken],
94
+ };
95
+ return {
96
+ module: ShieldModule_1,
97
+ // Same single-path composition as forRoot (+ the caller's option imports).
98
+ imports: [enforcement_module_js_1.EnforcementModule, boot_module_js_1.BootModule, ...(options.imports ?? [])],
99
+ providers: [asyncOptionsProvider, configProvider, authorityProvider],
100
+ exports: [config_module_js_1.APP_CONFIG, exports.SHIELD_DECISION_AUTHORITY],
101
+ };
102
+ }
103
+ };
104
+ exports.ShieldModule = ShieldModule;
105
+ exports.ShieldModule = ShieldModule = ShieldModule_1 = __decorate([
106
+ (0, common_1.Global)(),
107
+ (0, common_1.Module)({})
108
+ ], ShieldModule);
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `@astram/guard` crypto + clock adapters for the Session engine PORTs.
3
+ *
4
+ * ADR-0003 (why HERE, not Core): these need `node:crypto` — a runtime capability
5
+ * Core (zero-dep, `types: []`, node-free) cannot import. Core defines the
6
+ * {@link TokenGenerator} / {@link RefreshTokenHasher} / {@link Clock} PORTs; these
7
+ * concretes are INJECTED into `createSessionAuthority(...)`.
8
+ *
9
+ * ⛔ SECURITY — none of these ever logs a token. `CryptoTokenGenerator` mints
10
+ * high-entropy opaque tokens; `Sha256RefreshTokenHasher` is a KEYED HMAC (a leaked
11
+ * DB cannot be brute-forced as cheaply as a bare digest) and is deterministic so
12
+ * `findRefreshByHash` matches.
13
+ */
14
+ import type { Clock, RefreshTokenHasher, TokenGenerator } from '@astram/core';
15
+ /** DI tokens the ShieldModule / a consumer binds the concretes under. */
16
+ export declare const SHIELD_TOKEN_GENERATOR: unique symbol;
17
+ export declare const SHIELD_REFRESH_HASHER: unique symbol;
18
+ export declare const SHIELD_CLOCK: unique symbol;
19
+ /**
20
+ * Opaque random-token minter (node:crypto `randomBytes`). 32 bytes = 256 bits of
21
+ * entropy, hex-encoded to a 64-char string (matches the CHAR(64) columns). The
22
+ * engine treats the result as opaque; it never parses it.
23
+ */
24
+ export declare class CryptoTokenGenerator implements TokenGenerator {
25
+ private readonly byteLength;
26
+ constructor(byteLength?: number);
27
+ mint(): string;
28
+ }
29
+ /**
30
+ * Keyed HMAC-SHA-256 refresh-token hasher. Deterministic (same raw → same hash)
31
+ * so a lookup by hash matches, and keyed so an offline attacker with the DB but
32
+ * not the key cannot recompute hashes. Output is 64-char hex (CHAR(64)).
33
+ *
34
+ * ⛔ The `key` is a server secret (env/secrets-manager in prod). NEVER log the raw
35
+ * token or the key. A missing/empty key throws at construction (fail-closed: never
36
+ * silently downgrade to an unkeyed digest).
37
+ */
38
+ export declare class Sha256RefreshTokenHasher implements RefreshTokenHasher {
39
+ private readonly key;
40
+ constructor(key: string);
41
+ hash(rawToken: string): string;
42
+ }
43
+ /** The real clock (UTC epoch ms via `Date.now`). */
44
+ export declare class SystemClock implements Clock {
45
+ nowMs(): number;
46
+ }
47
+ /** Provider factories for wiring the crypto/clock adapters into a Nest container. */
48
+ export declare function cryptoTokenGeneratorProvider(byteLength?: number): {
49
+ provide: symbol;
50
+ useValue: CryptoTokenGenerator;
51
+ };
52
+ export declare function refreshHasherProvider(key: string): {
53
+ provide: symbol;
54
+ useValue: Sha256RefreshTokenHasher;
55
+ };
56
+ export declare function systemClockProvider(): {
57
+ provide: symbol;
58
+ useValue: SystemClock;
59
+ };
60
+ //# sourceMappingURL=crypto-adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto-adapters.d.ts","sourceRoot":"","sources":["../../src/session/crypto-adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9E,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,eAAmC,CAAC;AACvE,eAAO,MAAM,qBAAqB,eAAkC,CAAC;AACrE,eAAO,MAAM,YAAY,eAAyB,CAAC;AAEnD;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,SAAK;IAE5C,IAAI,IAAI,MAAM;CAIf;AAED;;;;;;;;GAQG;AACH,qBAAa,wBAAyB,YAAW,kBAAkB;IACjE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,GAAG,EAAE,MAAM;IASvB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAI/B;AAED,oDAAoD;AACpD,qBAAa,WAAY,YAAW,KAAK;IACvC,KAAK,IAAI,MAAM;CAGhB;AAED,qFAAqF;AACrF,wBAAgB,4BAA4B,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAEA;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,wBAAwB,CAAC;CACpC,CAEA;AAED,wBAAgB,mBAAmB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,WAAW,CAAA;CAAE,CAEhF"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * `@astram/guard` crypto + clock adapters for the Session engine PORTs.
4
+ *
5
+ * ADR-0003 (why HERE, not Core): these need `node:crypto` — a runtime capability
6
+ * Core (zero-dep, `types: []`, node-free) cannot import. Core defines the
7
+ * {@link TokenGenerator} / {@link RefreshTokenHasher} / {@link Clock} PORTs; these
8
+ * concretes are INJECTED into `createSessionAuthority(...)`.
9
+ *
10
+ * ⛔ SECURITY — none of these ever logs a token. `CryptoTokenGenerator` mints
11
+ * high-entropy opaque tokens; `Sha256RefreshTokenHasher` is a KEYED HMAC (a leaked
12
+ * DB cannot be brute-forced as cheaply as a bare digest) and is deterministic so
13
+ * `findRefreshByHash` matches.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.SystemClock = exports.Sha256RefreshTokenHasher = exports.CryptoTokenGenerator = exports.SHIELD_CLOCK = exports.SHIELD_REFRESH_HASHER = exports.SHIELD_TOKEN_GENERATOR = void 0;
17
+ exports.cryptoTokenGeneratorProvider = cryptoTokenGeneratorProvider;
18
+ exports.refreshHasherProvider = refreshHasherProvider;
19
+ exports.systemClockProvider = systemClockProvider;
20
+ const node_crypto_1 = require("node:crypto");
21
+ /** DI tokens the ShieldModule / a consumer binds the concretes under. */
22
+ exports.SHIELD_TOKEN_GENERATOR = Symbol('SHIELD_TOKEN_GENERATOR');
23
+ exports.SHIELD_REFRESH_HASHER = Symbol('SHIELD_REFRESH_HASHER');
24
+ exports.SHIELD_CLOCK = Symbol('SHIELD_CLOCK');
25
+ /**
26
+ * Opaque random-token minter (node:crypto `randomBytes`). 32 bytes = 256 bits of
27
+ * entropy, hex-encoded to a 64-char string (matches the CHAR(64) columns). The
28
+ * engine treats the result as opaque; it never parses it.
29
+ */
30
+ class CryptoTokenGenerator {
31
+ byteLength;
32
+ constructor(byteLength = 32) {
33
+ this.byteLength = byteLength;
34
+ }
35
+ mint() {
36
+ // 256-bit unguessable token. Hex so it is URL/DB-safe and fixed-width.
37
+ return (0, node_crypto_1.randomBytes)(this.byteLength).toString('hex');
38
+ }
39
+ }
40
+ exports.CryptoTokenGenerator = CryptoTokenGenerator;
41
+ /**
42
+ * Keyed HMAC-SHA-256 refresh-token hasher. Deterministic (same raw → same hash)
43
+ * so a lookup by hash matches, and keyed so an offline attacker with the DB but
44
+ * not the key cannot recompute hashes. Output is 64-char hex (CHAR(64)).
45
+ *
46
+ * ⛔ The `key` is a server secret (env/secrets-manager in prod). NEVER log the raw
47
+ * token or the key. A missing/empty key throws at construction (fail-closed: never
48
+ * silently downgrade to an unkeyed digest).
49
+ */
50
+ class Sha256RefreshTokenHasher {
51
+ key;
52
+ constructor(key) {
53
+ if (typeof key !== 'string' || key.length === 0) {
54
+ throw new Error('Sha256RefreshTokenHasher: a non-empty HMAC key is required (fail-closed — never unkeyed)');
55
+ }
56
+ this.key = Buffer.from(key, 'utf8');
57
+ }
58
+ hash(rawToken) {
59
+ // HMAC-SHA-256 → 64-char hex. Deterministic; never logs the input.
60
+ return (0, node_crypto_1.createHmac)('sha256', this.key).update(rawToken, 'utf8').digest('hex');
61
+ }
62
+ }
63
+ exports.Sha256RefreshTokenHasher = Sha256RefreshTokenHasher;
64
+ /** The real clock (UTC epoch ms via `Date.now`). */
65
+ class SystemClock {
66
+ nowMs() {
67
+ return Date.now();
68
+ }
69
+ }
70
+ exports.SystemClock = SystemClock;
71
+ /** Provider factories for wiring the crypto/clock adapters into a Nest container. */
72
+ function cryptoTokenGeneratorProvider(byteLength) {
73
+ return { provide: exports.SHIELD_TOKEN_GENERATOR, useValue: new CryptoTokenGenerator(byteLength) };
74
+ }
75
+ function refreshHasherProvider(key) {
76
+ return { provide: exports.SHIELD_REFRESH_HASHER, useValue: new Sha256RefreshTokenHasher(key) };
77
+ }
78
+ function systemClockProvider() {
79
+ return { provide: exports.SHIELD_CLOCK, useValue: new SystemClock() };
80
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@astram/guard` Session adapters barrel (ADR-0003 / ADR-0013).
3
+ *
4
+ * The concrete implementations of the Core Session PORTs — the real IO/crypto that
5
+ * keeps `@astram/core` zero-dep:
6
+ * - {@link MariaDbSessionRecordStore} — durable session/refresh records (mariadb);
7
+ * - {@link RedisHotSessionStore} — hot per-request state (ioredis);
8
+ * - {@link CryptoTokenGenerator} — opaque token minter (node:crypto);
9
+ * - {@link Sha256RefreshTokenHasher} — keyed HMAC-SHA-256 refresh hasher;
10
+ * - {@link SystemClock} — real UTC clock.
11
+ *
12
+ * A consumer wires these + `createSessionAuthority(...)` from `@astram/core`.
13
+ */
14
+ export { MariaDbSessionRecordStore, SHIELD_SESSION_RECORD_STORE, } from './mariadb-session-record-store.js';
15
+ export { RedisHotSessionStore, SHIELD_HOT_SESSION_STORE, } from './redis-hot-session-store.js';
16
+ export { CryptoTokenGenerator, Sha256RefreshTokenHasher, SystemClock, cryptoTokenGeneratorProvider, refreshHasherProvider, systemClockProvider, SHIELD_TOKEN_GENERATOR, SHIELD_REFRESH_HASHER, SHIELD_CLOCK, } from './crypto-adapters.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EACL,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,WAAW,EACX,4BAA4B,EAC5B,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,YAAY,GACb,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * `@astram/guard` Session adapters barrel (ADR-0003 / ADR-0013).
4
+ *
5
+ * The concrete implementations of the Core Session PORTs — the real IO/crypto that
6
+ * keeps `@astram/core` zero-dep:
7
+ * - {@link MariaDbSessionRecordStore} — durable session/refresh records (mariadb);
8
+ * - {@link RedisHotSessionStore} — hot per-request state (ioredis);
9
+ * - {@link CryptoTokenGenerator} — opaque token minter (node:crypto);
10
+ * - {@link Sha256RefreshTokenHasher} — keyed HMAC-SHA-256 refresh hasher;
11
+ * - {@link SystemClock} — real UTC clock.
12
+ *
13
+ * A consumer wires these + `createSessionAuthority(...)` from `@astram/core`.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.SHIELD_CLOCK = exports.SHIELD_REFRESH_HASHER = exports.SHIELD_TOKEN_GENERATOR = exports.systemClockProvider = exports.refreshHasherProvider = exports.cryptoTokenGeneratorProvider = exports.SystemClock = exports.Sha256RefreshTokenHasher = exports.CryptoTokenGenerator = exports.SHIELD_HOT_SESSION_STORE = exports.RedisHotSessionStore = exports.SHIELD_SESSION_RECORD_STORE = exports.MariaDbSessionRecordStore = void 0;
17
+ var mariadb_session_record_store_js_1 = require("./mariadb-session-record-store.js");
18
+ Object.defineProperty(exports, "MariaDbSessionRecordStore", { enumerable: true, get: function () { return mariadb_session_record_store_js_1.MariaDbSessionRecordStore; } });
19
+ Object.defineProperty(exports, "SHIELD_SESSION_RECORD_STORE", { enumerable: true, get: function () { return mariadb_session_record_store_js_1.SHIELD_SESSION_RECORD_STORE; } });
20
+ var redis_hot_session_store_js_1 = require("./redis-hot-session-store.js");
21
+ Object.defineProperty(exports, "RedisHotSessionStore", { enumerable: true, get: function () { return redis_hot_session_store_js_1.RedisHotSessionStore; } });
22
+ Object.defineProperty(exports, "SHIELD_HOT_SESSION_STORE", { enumerable: true, get: function () { return redis_hot_session_store_js_1.SHIELD_HOT_SESSION_STORE; } });
23
+ var crypto_adapters_js_1 = require("./crypto-adapters.js");
24
+ Object.defineProperty(exports, "CryptoTokenGenerator", { enumerable: true, get: function () { return crypto_adapters_js_1.CryptoTokenGenerator; } });
25
+ Object.defineProperty(exports, "Sha256RefreshTokenHasher", { enumerable: true, get: function () { return crypto_adapters_js_1.Sha256RefreshTokenHasher; } });
26
+ Object.defineProperty(exports, "SystemClock", { enumerable: true, get: function () { return crypto_adapters_js_1.SystemClock; } });
27
+ Object.defineProperty(exports, "cryptoTokenGeneratorProvider", { enumerable: true, get: function () { return crypto_adapters_js_1.cryptoTokenGeneratorProvider; } });
28
+ Object.defineProperty(exports, "refreshHasherProvider", { enumerable: true, get: function () { return crypto_adapters_js_1.refreshHasherProvider; } });
29
+ Object.defineProperty(exports, "systemClockProvider", { enumerable: true, get: function () { return crypto_adapters_js_1.systemClockProvider; } });
30
+ Object.defineProperty(exports, "SHIELD_TOKEN_GENERATOR", { enumerable: true, get: function () { return crypto_adapters_js_1.SHIELD_TOKEN_GENERATOR; } });
31
+ Object.defineProperty(exports, "SHIELD_REFRESH_HASHER", { enumerable: true, get: function () { return crypto_adapters_js_1.SHIELD_REFRESH_HASHER; } });
32
+ Object.defineProperty(exports, "SHIELD_CLOCK", { enumerable: true, get: function () { return crypto_adapters_js_1.SHIELD_CLOCK; } });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * `MariaDbSessionRecordStore` — the concrete durable {@link SessionRecordStore}
3
+ * for `@astram/guard`, backed by the `mariadb` connector against the Layer-2
4
+ * `shield_identity`.`session` / `shield_identity`.`refresh_token` tables
5
+ * (infra/mariadb/init/20-session-tables.sql).
6
+ *
7
+ * ADR-0003 (why HERE, not Core): the `mariadb` driver is a real runtime
8
+ * dependency; Core defines only the PORT. This adapter is INJECTED into
9
+ * `createSessionAuthority({ recordStore })`.
10
+ *
11
+ * ADR-0013/0014: written/read ONLY at lifecycle events (login/refresh/logout/admin
12
+ * revoke) — the per-request hot path never calls this. It authenticates as the
13
+ * runtime `shield_core` account, which holds DML (no DDL) on `shield_identity` —
14
+ * the containment model is untouched.
15
+ *
16
+ * ⛔ SECURITY — only ever receives + persists a token HASH (never a raw token).
17
+ * `revokeLineage` is the reuse kill-switch: it revokes EVERY refresh record + the
18
+ * session in a lineage in one transaction.
19
+ */
20
+ import type { Pool } from 'mariadb';
21
+ import type { CreateSessionInput, InsertRefreshInput, RefreshTokenRecord, SessionRecord, SessionRecordStore } from '@astram/core';
22
+ /** The DI token the store is bound under. */
23
+ export declare const SHIELD_SESSION_RECORD_STORE: unique symbol;
24
+ export declare class MariaDbSessionRecordStore implements SessionRecordStore {
25
+ private readonly pool;
26
+ constructor(pool: Pool);
27
+ createSession(input: CreateSessionInput): Promise<SessionRecord>;
28
+ insertRefreshRecord(input: InsertRefreshInput): Promise<RefreshTokenRecord>;
29
+ findRefreshByHash(tokenHash: string): Promise<RefreshTokenRecord | null>;
30
+ findSessionById(sessionId: string): Promise<SessionRecord | null>;
31
+ /**
32
+ * ⛔ ATOMIC ROTATION CLAIM (compare-and-set). The DB is the true arbiter: a
33
+ * conditional UPDATE that flips `active → rotated` ONLY when still active, and we
34
+ * return `affectedRows === 1` — exactly the caller that performed the transition.
35
+ * Under concurrency, two callers issue the same UPDATE; the storage engine
36
+ * serializes them, so ONE gets `affectedRows === 1` (the winner) and the other
37
+ * gets `0` (row already 'rotated' → loser). This makes rotation single-winner.
38
+ */
39
+ markRotatedIfActive(refreshId: string): Promise<boolean>;
40
+ markRevoked(refreshId: string): Promise<void>;
41
+ /**
42
+ * ⛔ THE REUSE KILL-SWITCH — atomically revoke EVERY refresh record AND the
43
+ * session in a lineage. One transaction so the kill is all-or-nothing.
44
+ */
45
+ revokeLineage(lineageId: string): Promise<void>;
46
+ revokeSession(sessionId: string): Promise<void>;
47
+ /** Run `fn` in a transaction, releasing the connection either way. */
48
+ private withTx;
49
+ }
50
+ //# sourceMappingURL=mariadb-session-record-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mariadb-session-record-store.d.ts","sourceRoot":"","sources":["../../src/session/mariadb-session-record-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAkB,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAGtB,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,eAAwC,CAAC;AAmDjF,qBAAa,yBAA0B,YAAW,kBAAkB;IACtD,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI;IAEjC,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IAiBhE,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA4B3E,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IASxE,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IASvE;;;;;;;OAOG;IACG,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASxD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQnD;;;OAGG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB/C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAarD,sEAAsE;YACxD,MAAM;CAiBrB"}
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ /**
3
+ * `MariaDbSessionRecordStore` — the concrete durable {@link SessionRecordStore}
4
+ * for `@astram/guard`, backed by the `mariadb` connector against the Layer-2
5
+ * `shield_identity`.`session` / `shield_identity`.`refresh_token` tables
6
+ * (infra/mariadb/init/20-session-tables.sql).
7
+ *
8
+ * ADR-0003 (why HERE, not Core): the `mariadb` driver is a real runtime
9
+ * dependency; Core defines only the PORT. This adapter is INJECTED into
10
+ * `createSessionAuthority({ recordStore })`.
11
+ *
12
+ * ADR-0013/0014: written/read ONLY at lifecycle events (login/refresh/logout/admin
13
+ * revoke) — the per-request hot path never calls this. It authenticates as the
14
+ * runtime `shield_core` account, which holds DML (no DDL) on `shield_identity` —
15
+ * the containment model is untouched.
16
+ *
17
+ * ⛔ SECURITY — only ever receives + persists a token HASH (never a raw token).
18
+ * `revokeLineage` is the reuse kill-switch: it revokes EVERY refresh record + the
19
+ * session in a lineage in one transaction.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.MariaDbSessionRecordStore = exports.SHIELD_SESSION_RECORD_STORE = void 0;
23
+ /** The DI token the store is bound under. */
24
+ exports.SHIELD_SESSION_RECORD_STORE = Symbol('SHIELD_SESSION_RECORD_STORE');
25
+ /** BIGINT columns can arrive as string; normalize to number safely. */
26
+ function toNum(v) {
27
+ return typeof v === 'number' ? v : Number(v);
28
+ }
29
+ function mapSession(r) {
30
+ return {
31
+ sessionId: r.s_session_id,
32
+ principalId: r.s_principal_id,
33
+ tenantId: r.s_tenant_id,
34
+ status: r.s_status,
35
+ issuedAtMs: toNum(r.s_issued_at_ms),
36
+ absoluteExpiresAtMs: toNum(r.s_absolute_expires_at_ms),
37
+ };
38
+ }
39
+ function mapRefresh(r) {
40
+ return {
41
+ refreshId: r.rt_refresh_id,
42
+ sessionId: r.rt_session_id,
43
+ lineageId: r.rt_lineage_id,
44
+ prevRefreshId: r.rt_prev_refresh_id,
45
+ tokenHash: r.rt_token_hash,
46
+ status: r.rt_status,
47
+ issuedAtMs: toNum(r.rt_issued_at_ms),
48
+ idleExpiresAtMs: toNum(r.rt_idle_expires_at_ms),
49
+ };
50
+ }
51
+ class MariaDbSessionRecordStore {
52
+ pool;
53
+ constructor(pool) {
54
+ this.pool = pool;
55
+ }
56
+ async createSession(input) {
57
+ await this.pool.query(`INSERT INTO shield_identity.session
58
+ (s_session_id, s_principal_id, s_tenant_id, s_status, s_issued_at_ms, s_absolute_expires_at_ms)
59
+ VALUES (?, ?, ?, 'active', ?, ?)`, [input.sessionId, input.principalId, input.tenantId, input.issuedAtMs, input.absoluteExpiresAtMs]);
60
+ return {
61
+ sessionId: input.sessionId,
62
+ principalId: input.principalId,
63
+ tenantId: input.tenantId,
64
+ status: 'active',
65
+ issuedAtMs: input.issuedAtMs,
66
+ absoluteExpiresAtMs: input.absoluteExpiresAtMs,
67
+ };
68
+ }
69
+ async insertRefreshRecord(input) {
70
+ await this.pool.query(`INSERT INTO shield_identity.refresh_token
71
+ (rt_refresh_id, rt_session_id, rt_lineage_id, rt_prev_refresh_id,
72
+ rt_token_hash, rt_status, rt_issued_at_ms, rt_idle_expires_at_ms)
73
+ VALUES (?, ?, ?, ?, ?, 'active', ?, ?)`, [
74
+ input.refreshId,
75
+ input.sessionId,
76
+ input.lineageId,
77
+ input.prevRefreshId,
78
+ input.tokenHash, // ⛔ HASH only — never a raw token.
79
+ input.issuedAtMs,
80
+ input.idleExpiresAtMs,
81
+ ]);
82
+ return {
83
+ refreshId: input.refreshId,
84
+ sessionId: input.sessionId,
85
+ lineageId: input.lineageId,
86
+ prevRefreshId: input.prevRefreshId,
87
+ tokenHash: input.tokenHash,
88
+ status: 'active',
89
+ issuedAtMs: input.issuedAtMs,
90
+ idleExpiresAtMs: input.idleExpiresAtMs,
91
+ };
92
+ }
93
+ async findRefreshByHash(tokenHash) {
94
+ const rows = (await this.pool.query(`SELECT * FROM shield_identity.refresh_token WHERE rt_token_hash = ? LIMIT 1`, [tokenHash]));
95
+ const row = rows[0];
96
+ return row ? mapRefresh(row) : null;
97
+ }
98
+ async findSessionById(sessionId) {
99
+ const rows = (await this.pool.query(`SELECT * FROM shield_identity.session WHERE s_session_id = ? LIMIT 1`, [sessionId]));
100
+ const row = rows[0];
101
+ return row ? mapSession(row) : null;
102
+ }
103
+ /**
104
+ * ⛔ ATOMIC ROTATION CLAIM (compare-and-set). The DB is the true arbiter: a
105
+ * conditional UPDATE that flips `active → rotated` ONLY when still active, and we
106
+ * return `affectedRows === 1` — exactly the caller that performed the transition.
107
+ * Under concurrency, two callers issue the same UPDATE; the storage engine
108
+ * serializes them, so ONE gets `affectedRows === 1` (the winner) and the other
109
+ * gets `0` (row already 'rotated' → loser). This makes rotation single-winner.
110
+ */
111
+ async markRotatedIfActive(refreshId) {
112
+ const res = (await this.pool.query(`UPDATE shield_identity.refresh_token SET rt_status = 'rotated'
113
+ WHERE rt_refresh_id = ? AND rt_status = 'active'`, [refreshId]));
114
+ return res?.affectedRows === 1;
115
+ }
116
+ async markRevoked(refreshId) {
117
+ await this.pool.query(`UPDATE shield_identity.refresh_token SET rt_status = 'revoked'
118
+ WHERE rt_refresh_id = ?`, [refreshId]);
119
+ }
120
+ /**
121
+ * ⛔ THE REUSE KILL-SWITCH — atomically revoke EVERY refresh record AND the
122
+ * session in a lineage. One transaction so the kill is all-or-nothing.
123
+ */
124
+ async revokeLineage(lineageId) {
125
+ await this.withTx(async (conn) => {
126
+ await conn.query(`UPDATE shield_identity.refresh_token SET rt_status = 'revoked'
127
+ WHERE rt_lineage_id = ?`, [lineageId]);
128
+ await conn.query(`UPDATE shield_identity.session SET s_status = 'revoked'
129
+ WHERE s_session_id IN (
130
+ SELECT DISTINCT rt_session_id FROM shield_identity.refresh_token WHERE rt_lineage_id = ?
131
+ )`, [lineageId]);
132
+ });
133
+ }
134
+ async revokeSession(sessionId) {
135
+ await this.withTx(async (conn) => {
136
+ await conn.query(`UPDATE shield_identity.session SET s_status = 'revoked' WHERE s_session_id = ?`, [sessionId]);
137
+ await conn.query(`UPDATE shield_identity.refresh_token SET rt_status = 'revoked' WHERE rt_session_id = ?`, [sessionId]);
138
+ });
139
+ }
140
+ /** Run `fn` in a transaction, releasing the connection either way. */
141
+ async withTx(fn) {
142
+ const conn = await this.pool.getConnection();
143
+ try {
144
+ await conn.beginTransaction();
145
+ await fn(conn);
146
+ await conn.commit();
147
+ }
148
+ catch (err) {
149
+ try {
150
+ await conn.rollback();
151
+ }
152
+ catch {
153
+ // rollback best-effort; surface the original error below.
154
+ }
155
+ throw err;
156
+ }
157
+ finally {
158
+ conn.release();
159
+ }
160
+ }
161
+ }
162
+ exports.MariaDbSessionRecordStore = MariaDbSessionRecordStore;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * `RedisHotSessionStore` — the concrete hot {@link HotSessionStore} for
3
+ * `@astram/guard`, backed by `ioredis` against the Layer-2 Redis (ADR-0013 hot
4
+ * tier). Serves the per-request read path and is (re)populated at lifecycle events.
5
+ *
6
+ * ADR-0003 (why HERE, not Core): `ioredis` is a real runtime dependency; Core
7
+ * defines only the PORT. Injected into `createSessionAuthority({ hotStore })` and
8
+ * used by the free `readHotSession(hotStore, id)` per-request path.
9
+ *
10
+ * ⛔ THE CRITICAL CONTRACT (ADR-0013/0016): `getHotSession` MUST distinguish
11
+ * - `{ kind: 'found', state }` — a live hot record,
12
+ * - `{ kind: 'absent' }` — Redis is UP and simply has no such key (a miss),
13
+ * - `{ kind: 'unavailable' }` — Redis CANNOT answer (down/timeout/refused).
14
+ * The engine fails CLOSED on `unavailable` and NEVER falls back to the durable
15
+ * tier. Conflating unavailable→absent would fail open on a blip; conflating
16
+ * absent→unavailable would log users out on a normal miss. So we catch connection
17
+ * errors and map ONLY those to `unavailable`; a clean `null` GET reply is `absent`.
18
+ */
19
+ import type Redis from 'ioredis';
20
+ import type { HotSessionOutcome, HotSessionState, HotSessionStore } from '@astram/core';
21
+ /** The DI token the store is bound under. */
22
+ export declare const SHIELD_HOT_SESSION_STORE: unique symbol;
23
+ export declare class RedisHotSessionStore implements HotSessionStore {
24
+ private readonly redis;
25
+ constructor(redis: Redis);
26
+ putHotSession(key: string, state: HotSessionState, ttlMs: number): Promise<void>;
27
+ getHotSession(key: string): Promise<HotSessionOutcome>;
28
+ deleteHotSession(key: string): Promise<void>;
29
+ }
30
+ //# sourceMappingURL=redis-hot-session-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-hot-session-store.d.ts","sourceRoot":"","sources":["../../src/session/redis-hot-session-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAExF,6CAA6C;AAC7C,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAgC3E,qBAAa,oBAAqB,YAAW,eAAe;IAC9C,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,KAAK;IAEnC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhF,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAyBtD,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQnD"}