@authhero/adapter-interfaces 3.4.1 → 3.6.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.
@@ -0,0 +1,18 @@
1
+ import { Rollout, RolloutInsert, RolloutUpdate } from "../types/Rollout";
2
+ import { ListParams } from "../types/ListParams";
3
+ export interface ListRolloutsResult {
4
+ rollouts: Rollout[];
5
+ start: number;
6
+ limit: number;
7
+ length: number;
8
+ }
9
+ export interface RolloutsAdapter {
10
+ /** Generates the `rol_<nanoid>` id and inserts with status `pending`. */
11
+ create(rollout: RolloutInsert): Promise<Rollout>;
12
+ get(id: string): Promise<Rollout | null>;
13
+ /** Default sort: `created_at` descending. */
14
+ list(params?: ListParams): Promise<ListRolloutsResult>;
15
+ /** Always bumps `updated_at`. */
16
+ update(id: string, rollout: RolloutUpdate): Promise<boolean>;
17
+ remove(id: string): Promise<boolean>;
18
+ }
@@ -0,0 +1,18 @@
1
+ import { TenantOperationEvent, TenantOperationEventInsert } from "../types/TenantOperationEvent";
2
+ import { ListParams } from "../types/ListParams";
3
+ export interface ListTenantOperationEventsResult {
4
+ events: TenantOperationEvent[];
5
+ start: number;
6
+ limit: number;
7
+ length: number;
8
+ }
9
+ /**
10
+ * Append-only step history for tenant operations — no update or remove;
11
+ * rows are deleted only via the cascade when their operation is removed.
12
+ */
13
+ export interface TenantOperationEventsAdapter {
14
+ /** Generates the `evt_<nanoid>` id. */
15
+ create(event: TenantOperationEventInsert): Promise<TenantOperationEvent>;
16
+ /** Ordered `created_at` ascending (id as tiebreak). */
17
+ listByOperation(operation_id: string, params?: ListParams): Promise<ListTenantOperationEventsResult>;
18
+ }
@@ -0,0 +1,34 @@
1
+ import { TenantOperation, TenantOperationEngine, TenantOperationInsert, TenantOperationKind, TenantOperationStatus, TenantOperationUpdate } from "../types/TenantOperation";
2
+ import { ListParams } from "../types/ListParams";
3
+ /**
4
+ * Tenant operations are control-plane entities (like `tenants` itself), so
5
+ * the adapter is unscoped: `tenant_id` is a list filter, not a scoping
6
+ * argument, and is null for fleet-level operations.
7
+ */
8
+ export interface ListTenantOperationsParams extends ListParams {
9
+ tenant_id?: string;
10
+ rollout_id?: string;
11
+ kind?: TenantOperationKind;
12
+ /** Single status or a set (the reconciler queries pending + running). */
13
+ status?: TenantOperationStatus | TenantOperationStatus[];
14
+ engine?: TenantOperationEngine;
15
+ /** Only operations whose `updated_at` is strictly before this ISO timestamp. */
16
+ updated_before?: string;
17
+ }
18
+ export interface ListTenantOperationsResult {
19
+ operations: TenantOperation[];
20
+ start: number;
21
+ limit: number;
22
+ length: number;
23
+ }
24
+ export interface TenantOperationsAdapter {
25
+ /** Generates the `op_<nanoid>` id and inserts with status `pending`. */
26
+ create(operation: TenantOperationInsert): Promise<TenantOperation>;
27
+ get(id: string): Promise<TenantOperation | null>;
28
+ /** Default sort: `created_at` descending. */
29
+ list(params?: ListTenantOperationsParams): Promise<ListTenantOperationsResult>;
30
+ /** Always bumps `updated_at`. */
31
+ update(id: string, operation: TenantOperationUpdate): Promise<boolean>;
32
+ /** Retention cleanup only — not exposed via routes; events cascade. */
33
+ remove(id: string): Promise<boolean>;
34
+ }
@@ -0,0 +1,9 @@
1
+ import { UserActivity, UserActivityUpdate } from "../types/UserActivity";
2
+ export interface UserActivityAdapter {
3
+ get(tenantId: string, userId: string): Promise<UserActivity | null>;
4
+ /**
5
+ * Insert or merge-update the activity row for a user. Only the provided
6
+ * fields are written; previously-stored fields are preserved.
7
+ */
8
+ upsert(tenantId: string, userId: string, activity: UserActivityUpdate): Promise<void>;
9
+ }
@@ -32,6 +32,7 @@ import { FormsAdapter } from "./Forms";
32
32
  import { ResourceServersAdapter } from "./ResourceServers";
33
33
  import { RolePermissionsAdapter } from "./RolePermissions";
34
34
  import { GrantsAdapter } from "./Grants";
35
+ import { UserActivityAdapter } from "./UserActivity";
35
36
  import { UserPermissionsAdapter } from "./UserPermissions";
36
37
  import { RolesAdapter } from "./Roles";
37
38
  import { UserRolesAdapter } from "./UserRoles";
@@ -49,6 +50,9 @@ import { EmailServiceAdapter } from "./EmailService";
49
50
  import { SmsServiceAdapter } from "./SmsService";
50
51
  import { OutboxAdapter } from "./Outbox";
51
52
  import { RateLimitAdapter } from "./RateLimit";
53
+ import { TenantOperationsAdapter } from "./TenantOperations";
54
+ import { TenantOperationEventsAdapter } from "./TenantOperationEvents";
55
+ import { RolloutsAdapter } from "./Rollouts";
52
56
  /**
53
57
  * Parameters for cleaning up expired sessions
54
58
  */
@@ -114,6 +118,14 @@ export interface DataAdapters {
114
118
  * `/api/v2/client-grants`, which is `clientGrants` above).
115
119
  */
116
120
  grants?: GrantsAdapter;
121
+ /**
122
+ * Optional write-often per-user activity counters (last_login, last_ip,
123
+ * login_count, …) split out of the `users` row (issue #1003). When set, the
124
+ * login flow double-writes these alongside the legacy `users` columns during
125
+ * the expand/contract migration. When undefined, only the legacy columns are
126
+ * written.
127
+ */
128
+ userActivity?: UserActivityAdapter;
117
129
  userPermissions: UserPermissionsAdapter;
118
130
  roles: RolesAdapter;
119
131
  sessions: SessionsAdapter;
@@ -125,6 +137,23 @@ export interface DataAdapters {
125
137
  */
126
138
  analytics?: AnalyticsAdapter;
127
139
  tenants: TenantsDataAdapter;
140
+ /**
141
+ * Optional control-plane log of durable tenant lifecycle operations
142
+ * (provision / seed / upgrade / backup / deprovision — issue #1026).
143
+ * The tenant row's provisioning fields remain the current-state snapshot;
144
+ * these rows are the append-only history explaining how it got there.
145
+ * When set (together with `tenantOperationEvents`), AuthHero mounts the
146
+ * `/api/v2/operations` and `/api/v2/tenants/:id/operations` management
147
+ * routes and lifecycle hooks record every provision run.
148
+ */
149
+ tenantOperations?: TenantOperationsAdapter;
150
+ /** Per-step history rows for `tenantOperations`; append-only. */
151
+ tenantOperationEvents?: TenantOperationEventsAdapter;
152
+ /**
153
+ * Optional fleet rollout records (issue #1026). Phase 1 ships the table
154
+ * and CRUD only; the wave/canary coordinator and routes come later.
155
+ */
156
+ rollouts?: RolloutsAdapter;
128
157
  themes: ThemesAdapter;
129
158
  universalLoginTemplates: UniversalLoginTemplatesAdapter;
130
159
  customText: CustomTextAdapter;
@@ -238,4 +267,8 @@ export * from "./TenantSettings";
238
267
  export * from "./Tenants";
239
268
  export * from "./Themes";
240
269
  export * from "./Grants";
270
+ export * from "./UserActivity";
241
271
  export * from "./Users";
272
+ export * from "./TenantOperations";
273
+ export * from "./TenantOperationEvents";
274
+ export * from "./Rollouts";
@@ -0,0 +1,77 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ /**
3
+ * Fleet operation coordinating per-tenant operations in waves with a canary
4
+ * and a health gate (issue #1026). Progress is derived by querying
5
+ * `tenant_operations` rows with this rollout's id — there are no
6
+ * denormalized counters.
7
+ */
8
+ export declare const rolloutKindSchema: z.ZodEnum<{
9
+ upgrade: "upgrade";
10
+ backup: "backup";
11
+ reseed: "reseed";
12
+ }>;
13
+ export type RolloutKind = z.infer<typeof rolloutKindSchema>;
14
+ export declare const rolloutStatusSchema: z.ZodEnum<{
15
+ pending: "pending";
16
+ failed: "failed";
17
+ paused: "paused";
18
+ canary: "canary";
19
+ rolling: "rolling";
20
+ done: "done";
21
+ }>;
22
+ export type RolloutStatus = z.infer<typeof rolloutStatusSchema>;
23
+ export declare const rolloutInsertSchema: z.ZodObject<{
24
+ kind: z.ZodEnum<{
25
+ upgrade: "upgrade";
26
+ backup: "backup";
27
+ reseed: "reseed";
28
+ }>;
29
+ target_worker_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
30
+ target_database_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
31
+ wave_size: z.ZodDefault<z.ZodNumber>;
32
+ canary_tenant_ids: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
33
+ filter: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
34
+ initiated_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
35
+ }, z.core.$strip>;
36
+ export type RolloutInsert = z.input<typeof rolloutInsertSchema>;
37
+ export declare const rolloutSchema: z.ZodObject<{
38
+ kind: z.ZodEnum<{
39
+ upgrade: "upgrade";
40
+ backup: "backup";
41
+ reseed: "reseed";
42
+ }>;
43
+ target_worker_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
44
+ target_database_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
45
+ wave_size: z.ZodDefault<z.ZodNumber>;
46
+ canary_tenant_ids: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
47
+ filter: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
48
+ initiated_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
49
+ id: z.ZodString;
50
+ status: z.ZodEnum<{
51
+ pending: "pending";
52
+ failed: "failed";
53
+ paused: "paused";
54
+ canary: "canary";
55
+ rolling: "rolling";
56
+ done: "done";
57
+ }>;
58
+ created_at: z.ZodString;
59
+ updated_at: z.ZodString;
60
+ finished_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
61
+ }, z.core.$strip>;
62
+ export type Rollout = z.infer<typeof rolloutSchema>;
63
+ export declare const rolloutUpdateSchema: z.ZodObject<{
64
+ status: z.ZodOptional<z.ZodEnum<{
65
+ pending: "pending";
66
+ failed: "failed";
67
+ paused: "paused";
68
+ canary: "canary";
69
+ rolling: "rolling";
70
+ done: "done";
71
+ }>>;
72
+ filter: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>>;
73
+ finished_at: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
74
+ wave_size: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
75
+ canary_tenant_ids: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>>;
76
+ }, z.core.$strip>;
77
+ export type RolloutUpdate = z.infer<typeof rolloutUpdateSchema>;
@@ -0,0 +1,99 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ /**
3
+ * Durable tenant lifecycle operations (issue #1026). Each row is one
4
+ * provision / seed / upgrade / backup / deprovision run against a tenant
5
+ * (or the whole fleet when `tenant_id` is null). The tenant row's
6
+ * `provisioning_state` / `worker_version` / `database_version` remain the
7
+ * current-state snapshot; operations are the append-only log explaining how
8
+ * the snapshot got there.
9
+ */
10
+ export declare const tenantOperationKindSchema: z.ZodEnum<{
11
+ provision: "provision";
12
+ seed: "seed";
13
+ upgrade: "upgrade";
14
+ backup: "backup";
15
+ deprovision: "deprovision";
16
+ }>;
17
+ export type TenantOperationKind = z.infer<typeof tenantOperationKindSchema>;
18
+ export declare const tenantOperationStatusSchema: z.ZodEnum<{
19
+ pending: "pending";
20
+ failed: "failed";
21
+ running: "running";
22
+ succeeded: "succeeded";
23
+ cancelled: "cancelled";
24
+ }>;
25
+ export type TenantOperationStatus = z.infer<typeof tenantOperationStatusSchema>;
26
+ export declare const tenantOperationEngineSchema: z.ZodEnum<{
27
+ inline: "inline";
28
+ "cloudflare-workflows": "cloudflare-workflows";
29
+ }>;
30
+ export type TenantOperationEngine = z.infer<typeof tenantOperationEngineSchema>;
31
+ export declare const tenantOperationInsertSchema: z.ZodObject<{
32
+ tenant_id: z.ZodDefault<z.ZodNullable<z.ZodString>>;
33
+ rollout_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
34
+ kind: z.ZodEnum<{
35
+ provision: "provision";
36
+ seed: "seed";
37
+ upgrade: "upgrade";
38
+ backup: "backup";
39
+ deprovision: "deprovision";
40
+ }>;
41
+ engine: z.ZodEnum<{
42
+ inline: "inline";
43
+ "cloudflare-workflows": "cloudflare-workflows";
44
+ }>;
45
+ engine_instance_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
46
+ target_worker_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
47
+ target_database_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
48
+ initiated_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
49
+ }, z.core.$strip>;
50
+ export type TenantOperationInsert = z.input<typeof tenantOperationInsertSchema>;
51
+ export declare const tenantOperationSchema: z.ZodObject<{
52
+ tenant_id: z.ZodDefault<z.ZodNullable<z.ZodString>>;
53
+ rollout_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
54
+ kind: z.ZodEnum<{
55
+ provision: "provision";
56
+ seed: "seed";
57
+ upgrade: "upgrade";
58
+ backup: "backup";
59
+ deprovision: "deprovision";
60
+ }>;
61
+ engine: z.ZodEnum<{
62
+ inline: "inline";
63
+ "cloudflare-workflows": "cloudflare-workflows";
64
+ }>;
65
+ engine_instance_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
66
+ target_worker_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
67
+ target_database_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
68
+ initiated_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
69
+ id: z.ZodString;
70
+ status: z.ZodEnum<{
71
+ pending: "pending";
72
+ failed: "failed";
73
+ running: "running";
74
+ succeeded: "succeeded";
75
+ cancelled: "cancelled";
76
+ }>;
77
+ current_step: z.ZodOptional<z.ZodNullable<z.ZodString>>;
78
+ error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
79
+ created_at: z.ZodString;
80
+ updated_at: z.ZodString;
81
+ finished_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
82
+ }, z.core.$strip>;
83
+ export type TenantOperation = z.infer<typeof tenantOperationSchema>;
84
+ export declare const tenantOperationUpdateSchema: z.ZodObject<{
85
+ error: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
86
+ status: z.ZodOptional<z.ZodEnum<{
87
+ pending: "pending";
88
+ failed: "failed";
89
+ running: "running";
90
+ succeeded: "succeeded";
91
+ cancelled: "cancelled";
92
+ }>>;
93
+ engine_instance_id: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
94
+ target_worker_version: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
95
+ target_database_version: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
96
+ current_step: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
97
+ finished_at: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
98
+ }, z.core.$strip>;
99
+ export type TenantOperationUpdate = z.infer<typeof tenantOperationUpdateSchema>;
@@ -0,0 +1,48 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ /**
3
+ * Append-only per-step history for a tenant operation (issue #1026).
4
+ * One row per step boundary: started / succeeded / failed / retried /
5
+ * skipped, plus `reconciled` when the reconciler sweep copies a terminal
6
+ * engine state into the database after an instance died mid-run.
7
+ */
8
+ export declare const tenantOperationEventOutcomeSchema: z.ZodEnum<{
9
+ failed: "failed";
10
+ succeeded: "succeeded";
11
+ started: "started";
12
+ retried: "retried";
13
+ skipped: "skipped";
14
+ reconciled: "reconciled";
15
+ }>;
16
+ export type TenantOperationEventOutcome = z.infer<typeof tenantOperationEventOutcomeSchema>;
17
+ export declare const tenantOperationEventInsertSchema: z.ZodObject<{
18
+ operation_id: z.ZodString;
19
+ step: z.ZodString;
20
+ outcome: z.ZodEnum<{
21
+ failed: "failed";
22
+ succeeded: "succeeded";
23
+ started: "started";
24
+ retried: "retried";
25
+ skipped: "skipped";
26
+ reconciled: "reconciled";
27
+ }>;
28
+ detail: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
29
+ attempt: z.ZodDefault<z.ZodNumber>;
30
+ }, z.core.$strip>;
31
+ export type TenantOperationEventInsert = z.input<typeof tenantOperationEventInsertSchema>;
32
+ export declare const tenantOperationEventSchema: z.ZodObject<{
33
+ operation_id: z.ZodString;
34
+ step: z.ZodString;
35
+ outcome: z.ZodEnum<{
36
+ failed: "failed";
37
+ succeeded: "succeeded";
38
+ started: "started";
39
+ retried: "retried";
40
+ skipped: "skipped";
41
+ reconciled: "reconciled";
42
+ }>;
43
+ detail: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
44
+ attempt: z.ZodDefault<z.ZodNumber>;
45
+ id: z.ZodString;
46
+ created_at: z.ZodString;
47
+ }, z.core.$strip>;
48
+ export type TenantOperationEvent = z.infer<typeof tenantOperationEventSchema>;
@@ -0,0 +1,17 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ /**
3
+ * Write-often per-user counters split out of the `users` row (issue #1003) so
4
+ * the profile row isn't rewritten on every login / failed password attempt.
5
+ * 1:1 with a user, keyed by `(tenant_id, user_id)`.
6
+ */
7
+ export declare const userActivitySchema: z.ZodObject<{
8
+ user_id: z.ZodString;
9
+ last_login: z.ZodOptional<z.ZodString>;
10
+ last_ip: z.ZodOptional<z.ZodString>;
11
+ login_count: z.ZodDefault<z.ZodNumber>;
12
+ failed_logins: z.ZodOptional<z.ZodArray<z.ZodString>>;
13
+ last_password_reset: z.ZodOptional<z.ZodString>;
14
+ }, z.core.$strip>;
15
+ export type UserActivity = z.infer<typeof userActivitySchema>;
16
+ /** Partial payload for an upsert — only the provided fields are written. */
17
+ export type UserActivityUpdate = Partial<Omit<UserActivity, "user_id">>;
@@ -29,6 +29,9 @@ export * from "./Password";
29
29
  export * from "./Session";
30
30
  export * from "./SigningKey";
31
31
  export * from "./Tenant";
32
+ export * from "./TenantOperation";
33
+ export * from "./TenantOperationEvent";
34
+ export * from "./Rollout";
32
35
  export * from "./Token";
33
36
  export * from "./User";
34
37
  export * from "./Theme";
@@ -41,6 +44,7 @@ export * from "./SmsProvider";
41
44
  export * from "./ResourceServer";
42
45
  export * from "./RolePermission";
43
46
  export * from "./Grant";
47
+ export * from "./UserActivity";
44
48
  export * from "./UserPermission";
45
49
  export * from "./UserRole";
46
50
  export * from "./Role";
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "git",
12
12
  "url": "https://github.com/markusahlstrand/authhero"
13
13
  },
14
- "version": "3.4.1",
14
+ "version": "3.6.0",
15
15
  "files": [
16
16
  "dist"
17
17
  ],