@authhero/adapter-interfaces 3.9.0 → 3.11.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,49 @@
1
+ /**
2
+ * Date conversion utilities shared by the SQL adapters (kysely, drizzle).
3
+ *
4
+ * Databases may hold timestamps in either of two formats during migration:
5
+ * - Old format: varchar(35) ISO 8601 strings (e.g., "2024-01-15T10:30:00.000Z")
6
+ * - New format: bigint Unix timestamps in milliseconds (e.g., 1705315800000),
7
+ * stored in `_ts`-suffixed columns
8
+ *
9
+ * The adapter interface always uses ISO strings, so adapters convert:
10
+ * - On READ: detect the stored format and convert to an ISO string
11
+ * - On WRITE: write as bigint timestamp (new format)
12
+ *
13
+ * This allows zero-downtime migration where:
14
+ * 1. Deploy code that reads both formats, writes new format
15
+ * 2. Run migration to convert existing data
16
+ * 3. (Optional) Remove old format support after migration completes
17
+ */
18
+ /**
19
+ * Type for a date field that could be either format from the database
20
+ */
21
+ export type DbDateField = string | number | null | undefined;
22
+ /**
23
+ * Convert a database date field (either format) to an ISO string for the
24
+ * adapter interface. Returns undefined if the value is null/undefined/empty.
25
+ */
26
+ export declare function dbDateToIso(value: DbDateField): string | undefined;
27
+ /**
28
+ * Convert a database date field to ISO string, with a required fallback.
29
+ * Use this for non-nullable date fields like created_at.
30
+ */
31
+ export declare function dbDateToIsoRequired(value: DbDateField, fallback?: string): string;
32
+ /**
33
+ * Convert an ISO string from the adapter interface to a bigint timestamp
34
+ * for writing to the database.
35
+ */
36
+ export declare function isoToDbDate(isoString: string | null | undefined): number | null;
37
+ /**
38
+ * Get current timestamp as ISO string (for adapter interface)
39
+ */
40
+ export declare function nowIso(): string;
41
+ /**
42
+ * Convert date fields from DB format to adapter format (ISO strings).
43
+ * Strips the _ts suffix from column names (e.g., created_at_ts -> created_at).
44
+ * @param row - The database row object
45
+ * @param requiredColumns - Columns that must have a value (will use fallback if null)
46
+ * @param optionalColumns - Columns that can be undefined
47
+ * @returns Object with converted date fields (with _ts suffix stripped)
48
+ */
49
+ export declare function convertDatesToAdapter<T extends Record<string, DbDateField>, R extends Extract<keyof T, string>, O extends Extract<keyof T, string> = never>(row: T, requiredColumns: R[], optionalColumns?: O[]): Record<string, string | undefined>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Helpers shared by the SQL adapters (kysely, drizzle), published as the
3
+ * `@authhero/adapter-interfaces/sql` subpath so they stay out of the main
4
+ * adapter-contract surface: non-SQL adapters (e.g. DynamoDB) never see them.
5
+ */
6
+ export * from "./dates";
7
+ export * from "./transform";
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Row/entity transformation helpers shared by the SQL adapters (kysely,
3
+ * drizzle). They convert between the adapter interface's rich entity shapes
4
+ * (nested objects, booleans, optional fields) and the flat SQL row shapes
5
+ * (JSON strings, 0/1 integers, no undefined values).
6
+ */
7
+ /**
8
+ * Stringify a value to JSON if it's defined, otherwise return undefined.
9
+ * This is useful for converting objects to JSON strings for database storage.
10
+ */
11
+ export declare function stringifyIfDefined<T>(value: T | undefined): string | undefined;
12
+ /**
13
+ * Stringify multiple properties of an object to JSON strings.
14
+ * Only properties that are defined will be stringified.
15
+ *
16
+ * @param obj - The source object containing properties to stringify
17
+ * @param properties - Array of property names to stringify
18
+ * @param target - The target object to write stringified values to (defaults to a new object based on obj)
19
+ * @returns The target object with stringified properties
20
+ *
21
+ * @example
22
+ * const source = { flags: { enabled: true }, sessions: { timeout: 300 }, name: "Test" };
23
+ * const result = stringifyProperties(source, ['flags', 'sessions']);
24
+ * // result = { flags: '{"enabled":true}', sessions: '{"timeout":300}', name: "Test" }
25
+ */
26
+ type StringifiedProperties<T, K extends keyof T> = {
27
+ [P in keyof T]: P extends K ? undefined extends T[P] ? string | undefined : string : T[P];
28
+ };
29
+ export declare function stringifyProperties<T extends Record<string, unknown>, K extends keyof T & string>(obj: T, properties: K[]): StringifiedProperties<T, K>;
30
+ export declare function stringifyProperties<T extends Record<string, unknown>, U extends Record<string, unknown>>(obj: T, properties: (keyof T & string)[], target: U): U;
31
+ /**
32
+ * Convert boolean properties to integers (1 for true, 0 for false).
33
+ * Only properties that are defined will be converted.
34
+ *
35
+ * @param source - The source object containing boolean properties to convert
36
+ * @param properties - Array of property names to convert
37
+ * @param target - The target object to write integer values to (defaults to source)
38
+ *
39
+ * @example
40
+ * const source = { enabled: true, active: false, name: "Test" };
41
+ * const result = {};
42
+ * booleanToInt(source, ['enabled', 'active'], result);
43
+ * // result = { enabled: 1, active: 0 }
44
+ */
45
+ export declare function booleanToInt<T extends Record<string, unknown>>(source: Partial<T>, properties: (keyof T & string)[], target?: Record<string, unknown>): void;
46
+ /**
47
+ * Remove undefined and null properties from an object.
48
+ * This keeps the SQL payload clean by only including defined values.
49
+ */
50
+ export declare function removeUndefinedAndNull<T extends Record<string, unknown>>(obj: T): Partial<T>;
51
+ /**
52
+ * Remove null properties from an object recursively. Used when mapping SQL
53
+ * rows (where missing values are NULL) back to adapter entities (where they
54
+ * are absent).
55
+ */
56
+ export declare function removeNullProperties<T>(obj: unknown): T;
57
+ /**
58
+ * Get a COUNT(*) result as an integer regardless of how the DB driver
59
+ * returns it (string, number, or bigint).
60
+ */
61
+ export declare function getCountAsInt(count: string | number | bigint): number;
62
+ export {};
@@ -4,10 +4,17 @@ export declare const totalsSchema: z.ZodObject<{
4
4
  limit: z.ZodNumber;
5
5
  length: z.ZodNumber;
6
6
  total: z.ZodOptional<z.ZodNumber>;
7
+ next: z.ZodOptional<z.ZodString>;
7
8
  }, z.core.$strip>;
8
9
  export interface Totals {
9
10
  start: number;
10
11
  limit: number;
11
12
  length: number;
12
13
  total?: number;
14
+ /**
15
+ * Opaque keyset cursor for the next page. Set only when the caller paginated
16
+ * with from/take and more rows may follow; absent on the last page and for
17
+ * offset (page/per_page) pagination.
18
+ */
19
+ next?: string;
13
20
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * URL-safe base64 ("base64url", RFC 4648 §5) without padding.
3
+ *
4
+ * This is the canonical base64url implementation for the AuthHero packages.
5
+ * It lives in `adapter-interfaces` (the lowest package) so every other package
6
+ * can depend on it, replacing scattered hand-rolled `btoa/atob` variants and
7
+ * the `oslo/encoding` dependency we are migrating away from.
8
+ *
9
+ * Both byte- and string-oriented helpers are provided:
10
+ * - `encodeBase64Url` / `decodeBase64Url` work on raw bytes (`Uint8Array`),
11
+ * matching how most callers use it (hashes, random bytes, JWT segments).
12
+ * - `encodeBase64UrlString` / `decodeBase64UrlString` wrap those with UTF-8
13
+ * (de)coding for the common "encode a JSON/text payload" case.
14
+ */
15
+ /** Encode raw bytes as a padding-free base64url string. */
16
+ export declare function encodeBase64Url(bytes: Uint8Array): string;
17
+ /** Decode a base64url string (with or without padding) back to raw bytes. */
18
+ export declare function decodeBase64Url(input: string): Uint8Array;
19
+ /** Encode a UTF-8 string as a padding-free base64url string. */
20
+ export declare function encodeBase64UrlString(input: string): string;
21
+ /** Decode a base64url string produced from UTF-8 text back to that string. */
22
+ export declare function decodeBase64UrlString(input: string): string;
@@ -0,0 +1,27 @@
1
+ export interface CursorPayload {
2
+ /**
3
+ * Value of the sort column on the last row of the previous page. `null` when
4
+ * that column was null on the boundary row. Absent for id-only ordering.
5
+ */
6
+ s?: string | number | null;
7
+ /** Id of the last row of the previous page — the unique tiebreaker. */
8
+ i: string;
9
+ /**
10
+ * Sort spec the cursor was minted under (e.g. `date:desc`). Set by endpoints
11
+ * that honor a caller-chosen sort in checkpoint mode, so a token replayed
12
+ * with a different sort is rejected instead of silently returning pages from
13
+ * the wrong position. Absent on fixed-sort endpoints.
14
+ */
15
+ k?: string;
16
+ }
17
+ /**
18
+ * Encode a keyset position into an opaque cursor token suitable for returning
19
+ * as `next` and accepting back as `from`.
20
+ */
21
+ export declare function encodeCursor(payload: CursorPayload): string;
22
+ /**
23
+ * Decode an opaque cursor token. Returns `null` for malformed or unparseable
24
+ * tokens so callers can fall back gracefully (e.g. start from the beginning)
25
+ * instead of throwing on client-supplied input.
26
+ */
27
+ export declare function decodeCursor(token: string): CursorPayload | null;
@@ -2,3 +2,6 @@ export * from "./user-id";
2
2
  export * from "./passthrough";
3
3
  export * from "./connection-attributes";
4
4
  export * from "./guards";
5
+ export * from "./base64url";
6
+ export * from "./cursor";
7
+ export * from "./lucene";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Shared pieces of the Lucene-style `q` filter handling. The SQL generation
3
+ * itself is ORM-specific and lives in each adapter; what is shared here is
4
+ * the query-string sanitization that enforces the tenant boundary.
5
+ */
6
+ export declare function sanitizeLuceneQuery(query: string, allowedFields: string[]): string;
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.9.0",
14
+ "version": "3.11.0",
15
15
  "files": [
16
16
  "dist"
17
17
  ],
@@ -20,9 +20,14 @@
20
20
  "types": "dist/adapter-interfaces.d.ts",
21
21
  "exports": {
22
22
  ".": {
23
+ "types": "./dist/adapter-interfaces.d.ts",
23
24
  "require": "./dist/adapter-interfaces.cjs",
24
- "import": "./dist/adapter-interfaces.mjs",
25
- "types": "./dist/adapter-interfaces.d.ts"
25
+ "import": "./dist/adapter-interfaces.mjs"
26
+ },
27
+ "./sql": {
28
+ "types": "./dist/sql.d.ts",
29
+ "require": "./dist/sql.cjs",
30
+ "import": "./dist/sql.mjs"
26
31
  }
27
32
  },
28
33
  "devDependencies": {