@authhero/adapter-interfaces 4.12.0 → 4.13.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.
- package/dist/adapter-interfaces.cjs +1 -1
- package/dist/adapter-interfaces.d.ts +208 -14
- package/dist/adapter-interfaces.mjs +660 -621
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/adapters/TenantOperationRows.d.ts +57 -0
- package/dist/types/adapters/TenantOperations.d.ts +30 -0
- package/dist/types/adapters/index.d.ts +8 -0
- package/dist/types/types/TenantOperation.d.ts +28 -12
- package/dist/types/types/TenantOperationRow.d.ts +86 -0
- package/dist/types/types/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { TenantOperationRow, TenantOperationRowCounts, TenantOperationRowInsert, TenantOperationRowOutcome, TenantOperationRowStatus } from "../types/TenantOperationRow";
|
|
2
|
+
import { ListParams } from "../types/ListParams";
|
|
3
|
+
export interface ListTenantOperationRowsParams extends ListParams {
|
|
4
|
+
/** Single status or a set (the errors endpoint queries `failed`). */
|
|
5
|
+
status?: TenantOperationRowStatus | TenantOperationRowStatus[];
|
|
6
|
+
}
|
|
7
|
+
export interface ListTenantOperationRowsResult {
|
|
8
|
+
rows: TenantOperationRow[];
|
|
9
|
+
start: number;
|
|
10
|
+
limit: number;
|
|
11
|
+
length: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Per-item checkpoints for batch tenant operations (issue #1325).
|
|
15
|
+
*
|
|
16
|
+
* Like `tenantOperations` this is a control-plane adapter, so it is
|
|
17
|
+
* unscoped — rows are addressed by `operation_id`, and the operation row
|
|
18
|
+
* carries the `tenant_id`.
|
|
19
|
+
*
|
|
20
|
+
* The contract that makes batch operations resumable: `createMany` stages
|
|
21
|
+
* every item as `pending`, `claimPending` hands a worker the next slice of
|
|
22
|
+
* unprocessed items, and `recordOutcomes` commits their terminal statuses.
|
|
23
|
+
* An interrupted run leaves its items `pending` and is retried verbatim,
|
|
24
|
+
* so implementations MUST make `recordOutcomes` atomic per call and MUST
|
|
25
|
+
* NOT mutate rows that already hold a terminal status.
|
|
26
|
+
*/
|
|
27
|
+
export interface TenantOperationRowsAdapter {
|
|
28
|
+
/**
|
|
29
|
+
* Stage items in bulk. Implementations should insert in batches rather
|
|
30
|
+
* than one statement per row — callers stage thousands at a time.
|
|
31
|
+
* Returns the number of rows written.
|
|
32
|
+
*/
|
|
33
|
+
createMany(rows: TenantOperationRowInsert[]): Promise<number>;
|
|
34
|
+
/**
|
|
35
|
+
* Fetch the next `limit` items still awaiting processing, ordered by
|
|
36
|
+
* `seq` ascending. Does not itself lock — exclusivity between drivers
|
|
37
|
+
* comes from the lease on the parent operation
|
|
38
|
+
* (`TenantOperationsAdapter.claim`).
|
|
39
|
+
*/
|
|
40
|
+
claimPending(operation_id: string, limit: number): Promise<TenantOperationRow[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Commit terminal outcomes for processed items. Idempotent: applying an
|
|
43
|
+
* outcome to a row that is already terminal is a no-op, so a retried
|
|
44
|
+
* chunk cannot corrupt earlier results. Returns rows actually updated.
|
|
45
|
+
*/
|
|
46
|
+
recordOutcomes(operation_id: string, outcomes: TenantOperationRowOutcome[]): Promise<number>;
|
|
47
|
+
/** Row counts per status, used to build the operation's summary. */
|
|
48
|
+
countByStatus(operation_id: string): Promise<TenantOperationRowCounts>;
|
|
49
|
+
/** Paginated read, ordered by `seq` ascending. Backs the errors endpoint. */
|
|
50
|
+
list(operation_id: string, params?: ListTenantOperationRowsParams): Promise<ListTenantOperationRowsResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Retention cleanup. Also runs via the cascade when the parent operation
|
|
53
|
+
* is removed; exposed separately so a sweep can drop staged payloads
|
|
54
|
+
* without deleting the operation's audit record.
|
|
55
|
+
*/
|
|
56
|
+
removeByOperation(operation_id: string): Promise<number>;
|
|
57
|
+
}
|
|
@@ -29,6 +29,36 @@ export interface TenantOperationsAdapter {
|
|
|
29
29
|
list(params?: ListTenantOperationsParams): Promise<ListTenantOperationsResult>;
|
|
30
30
|
/** Always bumps `updated_at`. */
|
|
31
31
|
update(id: string, operation: TenantOperationUpdate): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Atomically take the lease on an operation so only one driver advances
|
|
34
|
+
* it at a time. Succeeds when the operation is unclaimed or its existing
|
|
35
|
+
* lease has expired (or is already held by `worker_id`, making re-claim
|
|
36
|
+
* by the same worker a no-op); returns false when another live worker
|
|
37
|
+
* holds it.
|
|
38
|
+
*
|
|
39
|
+
* The lease is an efficiency guard, not a correctness guard — per-item
|
|
40
|
+
* checkpoints in `tenant_operation_rows` are what keep a batch operation
|
|
41
|
+
* correct if two drivers ever overlap. Implementations MUST perform the
|
|
42
|
+
* check and the write in a single conditional statement.
|
|
43
|
+
*/
|
|
44
|
+
claim(id: string, worker_id: string, leaseMs: number): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Release a lease held by `worker_id`. Returns false when the lease has
|
|
47
|
+
* since been taken by someone else, in which case the caller must not
|
|
48
|
+
* assume its own writes were the last ones.
|
|
49
|
+
*/
|
|
50
|
+
release(id: string, worker_id: string): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Operations that still have work to do and are free to be picked up:
|
|
53
|
+
* status `pending` or `running`, matching `kind`, whose lease is absent
|
|
54
|
+
* or expired. Ordered `created_at` ascending so the oldest unfinished
|
|
55
|
+
* work drains first. Backs the resume sweep that guarantees a batch
|
|
56
|
+
* completes even when every driver that touched it died.
|
|
57
|
+
*/
|
|
58
|
+
listResumable(params: {
|
|
59
|
+
kind: TenantOperationKind;
|
|
60
|
+
limit: number;
|
|
61
|
+
}): Promise<TenantOperation[]>;
|
|
32
62
|
/** Retention cleanup only — not exposed via routes; events cascade. */
|
|
33
63
|
remove(id: string): Promise<boolean>;
|
|
34
64
|
}
|
|
@@ -55,6 +55,7 @@ import { OutboxAdapter } from "./Outbox";
|
|
|
55
55
|
import { RateLimitAdapter } from "./RateLimit";
|
|
56
56
|
import { TenantOperationsAdapter } from "./TenantOperations";
|
|
57
57
|
import { TenantOperationEventsAdapter } from "./TenantOperationEvents";
|
|
58
|
+
import { TenantOperationRowsAdapter } from "./TenantOperationRows";
|
|
58
59
|
import { RolloutsAdapter } from "./Rollouts";
|
|
59
60
|
/**
|
|
60
61
|
* Parameters for cleaning up expired sessions
|
|
@@ -162,6 +163,12 @@ export interface DataAdapters {
|
|
|
162
163
|
tenantOperations?: TenantOperationsAdapter;
|
|
163
164
|
/** Per-step history rows for `tenantOperations`; append-only. */
|
|
164
165
|
tenantOperationEvents?: TenantOperationEventsAdapter;
|
|
166
|
+
/**
|
|
167
|
+
* Per-item checkpoints for batch operations (issue #1325). Required for
|
|
168
|
+
* the Auth0-compatible `POST /api/v2/jobs/users-imports` endpoints —
|
|
169
|
+
* without it, batch imports cannot be staged or resumed.
|
|
170
|
+
*/
|
|
171
|
+
tenantOperationRows?: TenantOperationRowsAdapter;
|
|
165
172
|
/**
|
|
166
173
|
* Optional fleet rollout records (issue #1026). Phase 1 ships the table
|
|
167
174
|
* and CRUD only; the wave/canary coordinator and routes come later.
|
|
@@ -287,4 +294,5 @@ export * from "./UserActivity";
|
|
|
287
294
|
export * from "./Users";
|
|
288
295
|
export * from "./TenantOperations";
|
|
289
296
|
export * from "./TenantOperationEvents";
|
|
297
|
+
export * from "./TenantOperationRows";
|
|
290
298
|
export * from "./Rollouts";
|
|
@@ -13,6 +13,7 @@ export declare const tenantOperationKindSchema: z.ZodEnum<{
|
|
|
13
13
|
upgrade: "upgrade";
|
|
14
14
|
backup: "backup";
|
|
15
15
|
deprovision: "deprovision";
|
|
16
|
+
users_import: "users_import";
|
|
16
17
|
}>;
|
|
17
18
|
export type TenantOperationKind = z.infer<typeof tenantOperationKindSchema>;
|
|
18
19
|
export declare const tenantOperationStatusSchema: z.ZodEnum<{
|
|
@@ -23,11 +24,22 @@ export declare const tenantOperationStatusSchema: z.ZodEnum<{
|
|
|
23
24
|
cancelled: "cancelled";
|
|
24
25
|
}>;
|
|
25
26
|
export type TenantOperationStatus = z.infer<typeof tenantOperationStatusSchema>;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Which driver advances an operation. Deliberately an open string, not an
|
|
29
|
+
* enum: the engine name is recorded in a core database column, so closing
|
|
30
|
+
* it here would bake vendor names into the schema and lock out
|
|
31
|
+
* self-hosters running their own driver (pg-boss, BullMQ, SQS, a plain
|
|
32
|
+
* cron). The values AuthHero ships are {@link KNOWN_TENANT_OPERATION_ENGINES}.
|
|
33
|
+
*
|
|
34
|
+
* Durability never depends on this value. Operation progress is checkpointed
|
|
35
|
+
* in the database (for `users_import`, one `tenant_operation_rows` row per
|
|
36
|
+
* user), so a crashed driver loses nothing and any other driver can resume
|
|
37
|
+
* the work.
|
|
38
|
+
*/
|
|
39
|
+
export declare const tenantOperationEngineSchema: z.ZodString;
|
|
30
40
|
export type TenantOperationEngine = z.infer<typeof tenantOperationEngineSchema>;
|
|
41
|
+
/** Engines AuthHero ships. Not exhaustive — the column accepts any name. */
|
|
42
|
+
export declare const KNOWN_TENANT_OPERATION_ENGINES: readonly ["inline", "cloudflare-workflows"];
|
|
31
43
|
export declare const tenantOperationInsertSchema: z.ZodObject<{
|
|
32
44
|
tenant_id: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
33
45
|
rollout_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -37,15 +49,14 @@ export declare const tenantOperationInsertSchema: z.ZodObject<{
|
|
|
37
49
|
upgrade: "upgrade";
|
|
38
50
|
backup: "backup";
|
|
39
51
|
deprovision: "deprovision";
|
|
52
|
+
users_import: "users_import";
|
|
40
53
|
}>;
|
|
41
|
-
engine: z.
|
|
42
|
-
inline: "inline";
|
|
43
|
-
"cloudflare-workflows": "cloudflare-workflows";
|
|
44
|
-
}>;
|
|
54
|
+
engine: z.ZodString;
|
|
45
55
|
engine_instance_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
46
56
|
target_worker_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
47
57
|
target_database_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
48
58
|
initiated_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
59
|
+
input: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
49
60
|
}, z.core.$strip>;
|
|
50
61
|
export type TenantOperationInsert = z.input<typeof tenantOperationInsertSchema>;
|
|
51
62
|
export declare const tenantOperationSchema: z.ZodObject<{
|
|
@@ -57,15 +68,14 @@ export declare const tenantOperationSchema: z.ZodObject<{
|
|
|
57
68
|
upgrade: "upgrade";
|
|
58
69
|
backup: "backup";
|
|
59
70
|
deprovision: "deprovision";
|
|
71
|
+
users_import: "users_import";
|
|
60
72
|
}>;
|
|
61
|
-
engine: z.
|
|
62
|
-
inline: "inline";
|
|
63
|
-
"cloudflare-workflows": "cloudflare-workflows";
|
|
64
|
-
}>;
|
|
73
|
+
engine: z.ZodString;
|
|
65
74
|
engine_instance_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
66
75
|
target_worker_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
67
76
|
target_database_version: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
68
77
|
initiated_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
78
|
+
input: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
69
79
|
id: z.ZodString;
|
|
70
80
|
status: z.ZodEnum<{
|
|
71
81
|
pending: "pending";
|
|
@@ -76,6 +86,9 @@ export declare const tenantOperationSchema: z.ZodObject<{
|
|
|
76
86
|
}>;
|
|
77
87
|
current_step: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
78
88
|
error: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
89
|
+
result: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
90
|
+
claimed_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
91
|
+
claim_expires_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
79
92
|
created_at: z.ZodString;
|
|
80
93
|
updated_at: z.ZodString;
|
|
81
94
|
finished_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -94,6 +107,9 @@ export declare const tenantOperationUpdateSchema: z.ZodObject<{
|
|
|
94
107
|
target_worker_version: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
95
108
|
target_database_version: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
96
109
|
current_step: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
110
|
+
result: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>>;
|
|
111
|
+
claimed_by: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
112
|
+
claim_expires_at: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
97
113
|
finished_at: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
98
114
|
}, z.core.$strip>;
|
|
99
115
|
export type TenantOperationUpdate = z.infer<typeof tenantOperationUpdateSchema>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from "@hono/zod-openapi";
|
|
2
|
+
/**
|
|
3
|
+
* Per-item checkpoint for a batch tenant operation (issue #1325).
|
|
4
|
+
*
|
|
5
|
+
* One row per item in the operation's input — for `users_import`, one per
|
|
6
|
+
* user in the uploaded file. This table is what makes a batch operation
|
|
7
|
+
* durable and resumable: an item is staged as `pending`, and its terminal
|
|
8
|
+
* status is committed in the same chunk that performs the write. A driver
|
|
9
|
+
* that dies mid-run leaves its unprocessed items `pending`, so any other
|
|
10
|
+
* driver (a cron sweep, a workflow step, a later request) resumes exactly
|
|
11
|
+
* where it stopped, without re-doing committed work.
|
|
12
|
+
*
|
|
13
|
+
* Durability therefore does not depend on the execution engine — engines
|
|
14
|
+
* only decide *who* advances the operation and *how fast*.
|
|
15
|
+
*/
|
|
16
|
+
export declare const tenantOperationRowStatusSchema: z.ZodEnum<{
|
|
17
|
+
pending: "pending";
|
|
18
|
+
failed: "failed";
|
|
19
|
+
skipped: "skipped";
|
|
20
|
+
inserted: "inserted";
|
|
21
|
+
updated: "updated";
|
|
22
|
+
}>;
|
|
23
|
+
export type TenantOperationRowStatus = z.infer<typeof tenantOperationRowStatusSchema>;
|
|
24
|
+
/** Terminal statuses — a row in any of these is never reprocessed. */
|
|
25
|
+
export declare const TERMINAL_TENANT_OPERATION_ROW_STATUSES: readonly TenantOperationRowStatus[];
|
|
26
|
+
export declare const tenantOperationRowInsertSchema: z.ZodObject<{
|
|
27
|
+
operation_id: z.ZodString;
|
|
28
|
+
seq: z.ZodNumber;
|
|
29
|
+
payload: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
30
|
+
status: z.ZodDefault<z.ZodEnum<{
|
|
31
|
+
pending: "pending";
|
|
32
|
+
failed: "failed";
|
|
33
|
+
skipped: "skipped";
|
|
34
|
+
inserted: "inserted";
|
|
35
|
+
updated: "updated";
|
|
36
|
+
}>>;
|
|
37
|
+
error_code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
38
|
+
error_message: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
39
|
+
error_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
|
+
entity_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
export type TenantOperationRowInsert = z.input<typeof tenantOperationRowInsertSchema>;
|
|
43
|
+
export declare const tenantOperationRowSchema: z.ZodObject<{
|
|
44
|
+
operation_id: z.ZodString;
|
|
45
|
+
seq: z.ZodNumber;
|
|
46
|
+
payload: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
47
|
+
error_code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
48
|
+
error_message: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
49
|
+
error_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
50
|
+
entity_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
51
|
+
status: z.ZodEnum<{
|
|
52
|
+
pending: "pending";
|
|
53
|
+
failed: "failed";
|
|
54
|
+
skipped: "skipped";
|
|
55
|
+
inserted: "inserted";
|
|
56
|
+
updated: "updated";
|
|
57
|
+
}>;
|
|
58
|
+
created_at: z.ZodString;
|
|
59
|
+
updated_at: z.ZodString;
|
|
60
|
+
}, z.core.$strip>;
|
|
61
|
+
export type TenantOperationRow = z.infer<typeof tenantOperationRowSchema>;
|
|
62
|
+
/** The per-item outcome a processor reports back for one staged row. */
|
|
63
|
+
export declare const tenantOperationRowOutcomeSchema: z.ZodObject<{
|
|
64
|
+
seq: z.ZodNumber;
|
|
65
|
+
status: z.ZodEnum<{
|
|
66
|
+
pending: "pending";
|
|
67
|
+
failed: "failed";
|
|
68
|
+
skipped: "skipped";
|
|
69
|
+
inserted: "inserted";
|
|
70
|
+
updated: "updated";
|
|
71
|
+
}>;
|
|
72
|
+
error_code: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
73
|
+
error_message: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
74
|
+
error_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
75
|
+
entity_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
export type TenantOperationRowOutcome = z.infer<typeof tenantOperationRowOutcomeSchema>;
|
|
78
|
+
/** Count of rows in each status, used to build a job summary. */
|
|
79
|
+
export interface TenantOperationRowCounts {
|
|
80
|
+
total: number;
|
|
81
|
+
pending: number;
|
|
82
|
+
inserted: number;
|
|
83
|
+
updated: number;
|
|
84
|
+
failed: number;
|
|
85
|
+
skipped: number;
|
|
86
|
+
}
|
|
@@ -31,6 +31,7 @@ export * from "./SigningKey";
|
|
|
31
31
|
export * from "./Tenant";
|
|
32
32
|
export * from "./TenantOperation";
|
|
33
33
|
export * from "./TenantOperationEvent";
|
|
34
|
+
export * from "./TenantOperationRow";
|
|
34
35
|
export * from "./Rollout";
|
|
35
36
|
export * from "./Token";
|
|
36
37
|
export * from "./User";
|