ofauth-shared-core 0.1.0-alpha.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 (56) hide show
  1. package/README.md +20 -0
  2. package/dist/OfauthCore.d.ts +48 -0
  3. package/dist/OfauthCore.js +200 -0
  4. package/dist/contracts/AuthAuditContract.d.ts +25 -0
  5. package/dist/contracts/AuthAuditContract.js +16 -0
  6. package/dist/contracts/AuthErrorContract.d.ts +5 -0
  7. package/dist/contracts/AuthErrorContract.js +2 -0
  8. package/dist/contracts/AuthPolicyContract.d.ts +25 -0
  9. package/dist/contracts/AuthPolicyContract.js +2 -0
  10. package/dist/contracts/ContextContract.d.ts +27 -0
  11. package/dist/contracts/ContextContract.js +8 -0
  12. package/dist/contracts/IdentityContract.d.ts +36 -0
  13. package/dist/contracts/IdentityContract.js +2 -0
  14. package/dist/contracts/SessionContract.d.ts +25 -0
  15. package/dist/contracts/SessionContract.js +10 -0
  16. package/dist/data/applyPendingMigrations.d.ts +2 -0
  17. package/dist/data/applyPendingMigrations.js +30 -0
  18. package/dist/data/migrations.d.ts +2 -0
  19. package/dist/data/migrations.js +47 -0
  20. package/dist/data/schemas.d.ts +11 -0
  21. package/dist/data/schemas.js +109 -0
  22. package/dist/index.d.ts +20 -0
  23. package/dist/index.js +36 -0
  24. package/dist/runtime/ContractStage.d.ts +2 -0
  25. package/dist/runtime/ContractStage.js +4 -0
  26. package/dist/services/AuthAuditService.d.ts +7 -0
  27. package/dist/services/AuthAuditService.js +2 -0
  28. package/dist/services/AuthHostComposition.d.ts +40 -0
  29. package/dist/services/AuthHostComposition.js +100 -0
  30. package/dist/services/AuthPolicyService.d.ts +9 -0
  31. package/dist/services/AuthPolicyService.js +2 -0
  32. package/dist/services/ContextService.d.ts +6 -0
  33. package/dist/services/ContextService.js +2 -0
  34. package/dist/services/IdentityService.d.ts +15 -0
  35. package/dist/services/IdentityService.js +2 -0
  36. package/dist/services/SessionService.d.ts +7 -0
  37. package/dist/services/SessionService.js +2 -0
  38. package/dist/services/createContractOnlyOfauthServices.d.ts +13 -0
  39. package/dist/services/createContractOnlyOfauthServices.js +82 -0
  40. package/dist/services/createDbAdapterOfauthServices.d.ts +20 -0
  41. package/dist/services/createDbAdapterOfauthServices.js +22 -0
  42. package/dist/services/errors.d.ts +8 -0
  43. package/dist/services/errors.js +20 -0
  44. package/dist/services/impl/DbAdapterAuthAuditService.d.ts +14 -0
  45. package/dist/services/impl/DbAdapterAuthAuditService.js +107 -0
  46. package/dist/services/impl/DbAdapterAuthPolicyService.d.ts +17 -0
  47. package/dist/services/impl/DbAdapterAuthPolicyService.js +114 -0
  48. package/dist/services/impl/DbAdapterContextService.d.ts +13 -0
  49. package/dist/services/impl/DbAdapterContextService.js +79 -0
  50. package/dist/services/impl/DbAdapterIdentityService.d.ts +23 -0
  51. package/dist/services/impl/DbAdapterIdentityService.js +146 -0
  52. package/dist/services/impl/DbAdapterSessionService.d.ts +14 -0
  53. package/dist/services/impl/DbAdapterSessionService.js +63 -0
  54. package/dist/services/impl/runtimeSupport.d.ts +95 -0
  55. package/dist/services/impl/runtimeSupport.js +112 -0
  56. package/package.json +37 -0
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # ofauth-shared-core
2
+
3
+ `ofauth-shared-core` adalah auth shared-core offline-first untuk ekosistem `of*`.
4
+
5
+ ## Fitur Inti
6
+ - identity/session/context contract,
7
+ - assurance/step-up contract,
8
+ - auth audit contract,
9
+ - runtime entrypoint `OfauthCore`.
10
+
11
+ ## Build and Validate
12
+ - `npm run build`
13
+ - `npm run verify:contract`
14
+ - `npm run verify:logic`
15
+ - `npm run ci:check`
16
+
17
+ ## Publish
18
+ 1. `npm run ci:check`
19
+ 2. `npm pack --dry-run`
20
+ 3. `npm publish --access public`
@@ -0,0 +1,48 @@
1
+ import type { CoreAdapterRegistry, CoreRuntime, CoreRuntimeHooks, CoreRuntimeStartOptions } from 'ofcore';
2
+ import type { DbAdapter, HttpAdapter, LoggerAdapter, PlatformAdapter, SocketAdapter, ActivitySink } from 'ofcore';
3
+ import type { ReadonlyStore, Store } from 'ofcore';
4
+ import type { OfauthDomainServices } from './services/createContractOnlyOfauthServices';
5
+ export type OfauthDomainServicesOverride = Partial<OfauthDomainServices>;
6
+ export type OfauthDomainServicesFactory = () => Promise<OfauthDomainServices> | OfauthDomainServices;
7
+ export type OfauthRuntimeHooks = Pick<CoreRuntimeHooks<OfauthDomainServices, never>, 'runMigrations' | 'runSeed' | 'onInit' | 'onStop'>;
8
+ export interface OfauthRuntimeState {
9
+ phase: 'idle' | 'starting' | 'started' | 'stopping' | 'stopped' | 'error';
10
+ started: boolean;
11
+ startCount: number;
12
+ stopCount: number;
13
+ lastError: string | null;
14
+ lastTransitionAt: string;
15
+ }
16
+ export declare class OfauthCore {
17
+ private readonly runtime;
18
+ private readonly runtimeStateStore;
19
+ domainServices?: OfauthDomainServices;
20
+ readonly runtimeStore: ReadonlyStore<OfauthRuntimeState>;
21
+ constructor(runtime: CoreRuntime<OfauthDomainServices, never>, runtimeStateStore: Store<OfauthRuntimeState>);
22
+ static builder(): OfauthCoreBuilder;
23
+ get registry(): CoreAdapterRegistry | undefined;
24
+ start(options?: CoreRuntimeStartOptions): Promise<void>;
25
+ init(options?: CoreRuntimeStartOptions): Promise<void>;
26
+ stop(): Promise<void>;
27
+ isStarted(): boolean;
28
+ }
29
+ export declare class OfauthCoreBuilder {
30
+ private readonly runtimeBuilder;
31
+ private domainServicesFactory?;
32
+ private domainServiceOverrides;
33
+ private runtimeHooks;
34
+ constructor();
35
+ withPlatformAdapter(factory: (core: CoreRuntime<any, any>) => PlatformAdapter): this;
36
+ withDbAdapter(factory: (core: CoreRuntime<any, any>) => DbAdapter): this;
37
+ withHttpAdapter(factory: (core: CoreRuntime<any, any>) => HttpAdapter): this;
38
+ withSocketAdapter(factory: (core: CoreRuntime<any, any>) => SocketAdapter): this;
39
+ withLoggerAdapter(factory: (core: CoreRuntime<any, any>) => LoggerAdapter): this;
40
+ withActivitySink(factory: (core: CoreRuntime<any, any>) => ActivitySink): this;
41
+ withExtension(name: string, factory: (core: CoreRuntime<any, any>) => unknown): this;
42
+ withDomainServicesFactory(factory: OfauthDomainServicesFactory): this;
43
+ overrideDomainServices(overrides: OfauthDomainServicesOverride): this;
44
+ withRuntimeHooks(hooks: Partial<OfauthRuntimeHooks>): this;
45
+ withMigrationRunner(runner: NonNullable<OfauthRuntimeHooks['runMigrations']>): this;
46
+ withSeedRunner(runner: NonNullable<OfauthRuntimeHooks['runSeed']>): this;
47
+ build(): OfauthCore;
48
+ }
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OfauthCoreBuilder = exports.OfauthCore = void 0;
4
+ const ofcore_1 = require("ofcore");
5
+ const ofcore_2 = require("ofcore");
6
+ const ofcore_3 = require("ofcore");
7
+ const createDbAdapterOfauthServices_1 = require("./services/createDbAdapterOfauthServices");
8
+ const applyPendingMigrations_1 = require("./data/applyPendingMigrations");
9
+ class OfauthCore {
10
+ constructor(runtime, runtimeStateStore) {
11
+ this.runtime = runtime;
12
+ this.runtimeStateStore = runtimeStateStore;
13
+ this.runtimeStore = (0, ofcore_3.asReadonlyStore)(this.runtimeStateStore);
14
+ }
15
+ static builder() {
16
+ return new OfauthCoreBuilder();
17
+ }
18
+ get registry() {
19
+ return this.runtime.registry;
20
+ }
21
+ async start(options = {}) {
22
+ this.runtimeStateStore.setState({
23
+ phase: 'starting',
24
+ started: false,
25
+ lastError: null,
26
+ lastTransitionAt: new Date().toISOString(),
27
+ });
28
+ try {
29
+ await this.runtime.start(options);
30
+ this.domainServices = this.runtime.domainServices;
31
+ this.runtimeStateStore.setState((state) => ({
32
+ ...state,
33
+ phase: 'started',
34
+ started: true,
35
+ startCount: state.startCount + 1,
36
+ lastError: null,
37
+ lastTransitionAt: new Date().toISOString(),
38
+ }));
39
+ }
40
+ catch (error) {
41
+ this.runtimeStateStore.setState({
42
+ phase: 'error',
43
+ started: false,
44
+ lastError: error instanceof Error ? error.message : String(error),
45
+ lastTransitionAt: new Date().toISOString(),
46
+ });
47
+ throw error;
48
+ }
49
+ }
50
+ async init(options = {}) {
51
+ this.runtimeStateStore.setState({
52
+ phase: 'starting',
53
+ started: false,
54
+ lastError: null,
55
+ lastTransitionAt: new Date().toISOString(),
56
+ });
57
+ try {
58
+ await this.runtime.init(options);
59
+ this.domainServices = this.runtime.domainServices;
60
+ this.runtimeStateStore.setState((state) => ({
61
+ ...state,
62
+ phase: 'started',
63
+ started: true,
64
+ startCount: state.startCount + 1,
65
+ lastError: null,
66
+ lastTransitionAt: new Date().toISOString(),
67
+ }));
68
+ }
69
+ catch (error) {
70
+ this.runtimeStateStore.setState({
71
+ phase: 'error',
72
+ started: false,
73
+ lastError: error instanceof Error ? error.message : String(error),
74
+ lastTransitionAt: new Date().toISOString(),
75
+ });
76
+ throw error;
77
+ }
78
+ }
79
+ async stop() {
80
+ this.runtimeStateStore.setState({
81
+ phase: 'stopping',
82
+ started: this.runtime.isStarted(),
83
+ lastError: null,
84
+ lastTransitionAt: new Date().toISOString(),
85
+ });
86
+ try {
87
+ await this.runtime.stop();
88
+ this.runtimeStateStore.setState((state) => ({
89
+ ...state,
90
+ phase: 'stopped',
91
+ started: false,
92
+ stopCount: state.stopCount + 1,
93
+ lastError: null,
94
+ lastTransitionAt: new Date().toISOString(),
95
+ }));
96
+ }
97
+ catch (error) {
98
+ this.runtimeStateStore.setState({
99
+ phase: 'error',
100
+ started: this.runtime.isStarted(),
101
+ lastError: error instanceof Error ? error.message : String(error),
102
+ lastTransitionAt: new Date().toISOString(),
103
+ });
104
+ throw error;
105
+ }
106
+ }
107
+ isStarted() {
108
+ return this.runtime.isStarted();
109
+ }
110
+ }
111
+ exports.OfauthCore = OfauthCore;
112
+ class OfauthCoreBuilder {
113
+ constructor() {
114
+ this.runtimeBuilder = ofcore_1.CoreRuntime.builder();
115
+ this.domainServiceOverrides = {};
116
+ this.runtimeHooks = {};
117
+ this.runtimeBuilder.withDbAdapter(() => new ofcore_2.InMemoryDbAdapter());
118
+ }
119
+ withPlatformAdapter(factory) {
120
+ this.runtimeBuilder.withPlatformAdapter(factory);
121
+ return this;
122
+ }
123
+ withDbAdapter(factory) {
124
+ this.runtimeBuilder.withDbAdapter(factory);
125
+ return this;
126
+ }
127
+ withHttpAdapter(factory) {
128
+ this.runtimeBuilder.withHttpAdapter(factory);
129
+ return this;
130
+ }
131
+ withSocketAdapter(factory) {
132
+ this.runtimeBuilder.withSocketAdapter(factory);
133
+ return this;
134
+ }
135
+ withLoggerAdapter(factory) {
136
+ this.runtimeBuilder.withLoggerAdapter(factory);
137
+ return this;
138
+ }
139
+ withActivitySink(factory) {
140
+ this.runtimeBuilder.withActivitySink(factory);
141
+ return this;
142
+ }
143
+ withExtension(name, factory) {
144
+ this.runtimeBuilder.withExtension(name, factory);
145
+ return this;
146
+ }
147
+ withDomainServicesFactory(factory) {
148
+ this.domainServicesFactory = factory;
149
+ return this;
150
+ }
151
+ overrideDomainServices(overrides) {
152
+ this.domainServiceOverrides = { ...this.domainServiceOverrides, ...overrides };
153
+ return this;
154
+ }
155
+ withRuntimeHooks(hooks) {
156
+ this.runtimeHooks = { ...this.runtimeHooks, ...hooks };
157
+ return this;
158
+ }
159
+ withMigrationRunner(runner) {
160
+ return this.withRuntimeHooks({ runMigrations: runner });
161
+ }
162
+ withSeedRunner(runner) {
163
+ return this.withRuntimeHooks({ runSeed: runner });
164
+ }
165
+ build() {
166
+ const runtimeStateStore = (0, ofcore_3.createStore)({
167
+ phase: 'idle',
168
+ started: false,
169
+ startCount: 0,
170
+ stopCount: 0,
171
+ lastError: null,
172
+ lastTransitionAt: new Date().toISOString(),
173
+ });
174
+ const defaultRunMigrations = async (runtime) => {
175
+ const dbAdapter = runtime.registry?.dbAdapter;
176
+ if (!dbAdapter)
177
+ return;
178
+ await (0, applyPendingMigrations_1.applyPendingMigrations)(dbAdapter, runtime.registry?.loggerAdapter);
179
+ };
180
+ this.runtimeBuilder.withHooks({
181
+ ...this.runtimeHooks,
182
+ runMigrations: this.runtimeHooks.runMigrations ?? defaultRunMigrations,
183
+ createDomainServices: async (runtime) => {
184
+ const base = this.domainServicesFactory
185
+ ? await this.domainServicesFactory()
186
+ : await (0, createDbAdapterOfauthServices_1.createDbAdapterOfauthServices)(runtime.registry?.dbAdapter ?? new ofcore_2.InMemoryDbAdapter(), {
187
+ logger: runtime.registry?.loggerAdapter,
188
+ platformAdapter: runtime.registry?.platformAdapter,
189
+ emitActivity: async (record) => {
190
+ await runtime.emitActivity(record);
191
+ },
192
+ });
193
+ return { ...base, ...this.domainServiceOverrides };
194
+ },
195
+ });
196
+ const runtime = this.runtimeBuilder.build();
197
+ return new OfauthCore(runtime, runtimeStateStore);
198
+ }
199
+ }
200
+ exports.OfauthCoreBuilder = OfauthCoreBuilder;
@@ -0,0 +1,25 @@
1
+ import type { AuthScopeRef } from './ContextContract';
2
+ export type AuthAuditEventType = 'LOGIN_SUCCESS' | 'LOGIN_FAILED' | 'LOCKOUT_TRIGGERED' | 'SESSION_REVOKED' | 'CONTEXT_SWITCHED' | 'STEP_UP_CHALLENGED' | 'STEP_UP_PASSED' | 'STEP_UP_FAILED';
3
+ export interface AuthAuditEvent {
4
+ eventId: string;
5
+ eventType: AuthAuditEventType;
6
+ identityId?: string;
7
+ sessionId?: string;
8
+ scopeRef?: AuthScopeRef;
9
+ result: 'success' | 'failed';
10
+ reasonCode?: string;
11
+ timestamp: string;
12
+ syncStatus?: 'pending' | 'replayed';
13
+ replayedAt?: string;
14
+ }
15
+ export interface AuthAuditQuery {
16
+ identityId?: string;
17
+ sessionId?: string;
18
+ eventTypes?: AuthAuditEventType[];
19
+ fromTimestamp?: string;
20
+ toTimestamp?: string;
21
+ }
22
+ export interface AuthAuditReplayQuery {
23
+ limit?: number;
24
+ }
25
+ export declare function isAuthAuditEventType(value: string): value is AuthAuditEventType;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAuthAuditEventType = isAuthAuditEventType;
4
+ function isAuthAuditEventType(value) {
5
+ const allowed = [
6
+ 'LOGIN_SUCCESS',
7
+ 'LOGIN_FAILED',
8
+ 'LOCKOUT_TRIGGERED',
9
+ 'SESSION_REVOKED',
10
+ 'CONTEXT_SWITCHED',
11
+ 'STEP_UP_CHALLENGED',
12
+ 'STEP_UP_PASSED',
13
+ 'STEP_UP_FAILED',
14
+ ];
15
+ return allowed.includes(value);
16
+ }
@@ -0,0 +1,5 @@
1
+ import type { ErrorEnvelope } from 'ofcore';
2
+ export type AuthErrorCode = 'AUTH_INVALID_CREDENTIAL' | 'AUTH_LOCKED' | 'AUTH_DISABLED' | 'AUTH_SESSION_NOT_FOUND' | 'AUTH_STEP_UP_REQUIRED' | 'AUTH_CONTEXT_FORBIDDEN' | 'AUTH_NOT_IMPLEMENTED';
3
+ export interface AuthErrorEnvelope extends ErrorEnvelope {
4
+ code: AuthErrorCode;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,25 @@
1
+ import type { AssuranceLevel } from './SessionContract';
2
+ export type AuthProfileId = 'cashier_fast' | 'supervisor_strict' | 'member_self_service';
3
+ export interface AuthProfile {
4
+ profileId: AuthProfileId;
5
+ defaultAssuranceLevel: AssuranceLevel;
6
+ lockoutPolicyRef: string;
7
+ }
8
+ export interface StepUpRequirement {
9
+ actionRef: string;
10
+ requiredAssuranceLevel: AssuranceLevel;
11
+ reauthWindowSeconds?: number;
12
+ }
13
+ export interface StepUpEvaluation {
14
+ actionRef: string;
15
+ requiredAssuranceLevel: AssuranceLevel;
16
+ currentAssuranceLevel: AssuranceLevel;
17
+ requiresStepUp: boolean;
18
+ reauthWindowSeconds?: number;
19
+ }
20
+ export interface AuthPolicySnapshot {
21
+ profiles: AuthProfile[];
22
+ stepUpRequirements: StepUpRequirement[];
23
+ version: string;
24
+ updatedAt: string;
25
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,27 @@
1
+ export interface AuthScopeRef {
2
+ tenantId?: string;
3
+ branchId?: string;
4
+ attributes?: Record<string, string>;
5
+ }
6
+ export interface ContextAssignment {
7
+ assignmentId: string;
8
+ identityId: string;
9
+ roleRef: string;
10
+ scopeRef: AuthScopeRef;
11
+ }
12
+ export interface ActiveContext {
13
+ sessionId: string;
14
+ assignmentId: string;
15
+ scopeRef: AuthScopeRef;
16
+ }
17
+ export interface ContextSwitchRequest {
18
+ sessionId: string;
19
+ targetAssignmentId: string;
20
+ reasonCode?: string;
21
+ }
22
+ export interface ContextSwitchResult {
23
+ activeContext: ActiveContext;
24
+ requiresStepUp: boolean;
25
+ reasonCode?: string;
26
+ }
27
+ export declare function hasAnyScopeDimension(scope: AuthScopeRef): boolean;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasAnyScopeDimension = hasAnyScopeDimension;
4
+ function hasAnyScopeDimension(scope) {
5
+ return Boolean(scope.tenantId ||
6
+ scope.branchId ||
7
+ (scope.attributes && Object.keys(scope.attributes).length > 0));
8
+ }
@@ -0,0 +1,36 @@
1
+ import type { AuthScopeRef, ContextAssignment } from './ContextContract';
2
+ export type IdentityStatus = 'active' | 'disabled' | 'locked';
3
+ export type CredentialVerifyMethod = 'pin' | 'password' | 'custom';
4
+ export interface IdentityRef {
5
+ identityId: string;
6
+ principal: string;
7
+ status: IdentityStatus;
8
+ }
9
+ export interface IdentityAssignmentRef {
10
+ assignmentId: string;
11
+ identityId: string;
12
+ scopeRef: AuthScopeRef;
13
+ roleRef: string;
14
+ }
15
+ export interface CredentialPolicy {
16
+ verifyMethod: CredentialVerifyMethod;
17
+ lockoutThreshold: number;
18
+ cooldownSeconds: number;
19
+ }
20
+ export interface CredentialVerifyRequest {
21
+ principal: string;
22
+ secret: string;
23
+ verifyMethod: CredentialVerifyMethod;
24
+ scopeRef?: AuthScopeRef;
25
+ }
26
+ export interface CredentialVerifySuccess {
27
+ ok: true;
28
+ identity: IdentityRef;
29
+ assignments: ContextAssignment[];
30
+ }
31
+ export interface CredentialVerifyFailure {
32
+ ok: false;
33
+ reasonCode: 'INVALID_CREDENTIAL' | 'IDENTITY_LOCKED' | 'IDENTITY_DISABLED';
34
+ retryable: boolean;
35
+ }
36
+ export type CredentialVerifyResult = CredentialVerifySuccess | CredentialVerifyFailure;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,25 @@
1
+ import type { AuthScopeRef } from './ContextContract';
2
+ export type AssuranceLevel = 'basic' | 'elevated';
3
+ export interface SessionRef {
4
+ sessionId: string;
5
+ identityId: string;
6
+ activeScopeRef?: AuthScopeRef;
7
+ assuranceLevel: AssuranceLevel;
8
+ issuedAt: string;
9
+ expiresAt: string;
10
+ }
11
+ export interface CreateSessionRequest {
12
+ identityId: string;
13
+ assignmentId?: string;
14
+ assuranceLevel?: AssuranceLevel;
15
+ ttlSeconds?: number;
16
+ }
17
+ export interface RefreshSessionRequest {
18
+ sessionId: string;
19
+ ttlSeconds?: number;
20
+ }
21
+ export interface RevokeSessionRequest {
22
+ sessionId: string;
23
+ reasonCode?: string;
24
+ }
25
+ export declare function isAssuranceLevelAtLeast(current: AssuranceLevel, required: AssuranceLevel): boolean;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAssuranceLevelAtLeast = isAssuranceLevelAtLeast;
4
+ function isAssuranceLevelAtLeast(current, required) {
5
+ const rank = {
6
+ basic: 1,
7
+ elevated: 2,
8
+ };
9
+ return rank[current] >= rank[required];
10
+ }
@@ -0,0 +1,2 @@
1
+ import type { DbAdapter, LoggerAdapter } from 'ofcore';
2
+ export declare function applyPendingMigrations(adapter: DbAdapter, logger?: LoggerAdapter): Promise<void>;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyPendingMigrations = applyPendingMigrations;
4
+ const migrations_1 = require("./migrations");
5
+ const noopLogger = {
6
+ logInfo() { },
7
+ logWarn() { },
8
+ logError() { },
9
+ };
10
+ async function applyPendingMigrations(adapter, logger = noopLogger) {
11
+ logger.logInfo('[ofauth] starting database migration process');
12
+ const sorted = [...migrations_1.migrations].sort((a, b) => a.toVersion - b.toVersion);
13
+ const targetVersion = sorted.length > 0 ? sorted[sorted.length - 1].toVersion : 0;
14
+ let currentVersion = await adapter.getSchemaVersion();
15
+ if (currentVersion == null || currentVersion < 0)
16
+ currentVersion = 0;
17
+ if (currentVersion >= targetVersion) {
18
+ logger.logInfo(`[ofauth] database already up-to-date at v${currentVersion}`);
19
+ return;
20
+ }
21
+ for (const migration of sorted) {
22
+ if (migration.toVersion > currentVersion) {
23
+ logger.logInfo(`[ofauth] applying migration v${migration.toVersion}`);
24
+ await migration.up(adapter);
25
+ await adapter.setSchemaVersion(migration.toVersion);
26
+ currentVersion = migration.toVersion;
27
+ }
28
+ }
29
+ logger.logInfo(`[ofauth] migration completed at v${currentVersion}`);
30
+ }
@@ -0,0 +1,2 @@
1
+ import type { Migration } from 'ofcore';
2
+ export declare const migrations: Migration[];
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.migrations = void 0;
4
+ const schemas_1 = require("./schemas");
5
+ exports.migrations = [
6
+ {
7
+ toVersion: 1,
8
+ up: async (adapter) => {
9
+ const tables = (0, schemas_1.getOfauthTableSchemas)();
10
+ for (const table of tables) {
11
+ try {
12
+ await adapter.addTable(table);
13
+ }
14
+ catch {
15
+ // keep migration idempotent for adapters that throw when table exists
16
+ }
17
+ }
18
+ },
19
+ },
20
+ {
21
+ toVersion: 2,
22
+ up: async (adapter) => {
23
+ try {
24
+ await adapter.addColumn(schemas_1.OFAUTH_TABLES.auditEvents, {
25
+ name: 'syncStatus',
26
+ type: 'string',
27
+ enum: ['pending', 'replayed'],
28
+ isIndexed: true,
29
+ });
30
+ }
31
+ catch {
32
+ // keep migration idempotent
33
+ }
34
+ try {
35
+ await adapter.addColumn(schemas_1.OFAUTH_TABLES.auditEvents, {
36
+ name: 'replayedAt',
37
+ type: 'string',
38
+ isOptional: true,
39
+ isIndexed: true,
40
+ });
41
+ }
42
+ catch {
43
+ // keep migration idempotent
44
+ }
45
+ },
46
+ },
47
+ ];
@@ -0,0 +1,11 @@
1
+ import type { DbSchema, TableSchema } from 'ofcore';
2
+ export declare const OFAUTH_TABLES: {
3
+ readonly identities: "ofauth_identities";
4
+ readonly assignments: "ofauth_assignments";
5
+ readonly sessions: "ofauth_sessions";
6
+ readonly policyProfiles: "ofauth_policy_profiles";
7
+ readonly policyStepups: "ofauth_policy_stepups";
8
+ readonly auditEvents: "ofauth_audit_events";
9
+ };
10
+ export declare const ofauthSchema: DbSchema;
11
+ export declare function getOfauthTableSchemas(): TableSchema[];