@awsmbiz/inbis-api-client 0.0.0-stage → 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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/api-error.ts","../src/api-client.ts","../src/role-admin.ts","../src/admin-routes.ts","../src/auth-settings.ts","../src/auth-admin-client.ts","../src/query.ts"],"sourcesContent":["/**\n * One error type for every failed call: app routes (the platform envelope\n * `{ error: { code, message, requestId, details? } }`) and auth routes\n * (Better Auth's `{ code, message, details? }`).\n */\n\n/** Codes the platform returns that callers commonly branch on. Any other server code passes through as is. */\nexport const API_ERROR_CODES = {\n /** No response: offline, DNS, CORS or the server is unreachable (`status` 0). */\n NETWORK_ERROR: \"NETWORK_ERROR\",\n /** Sign in again with one of `details.allowedMethods` (see {@link ApiError.requirement}). */\n STEP_UP_REQUIRED: \"STEP_UP_REQUIRED\",\n /** Complete two-factor authentication (or enrol an authenticator) to continue. */\n TWO_FACTOR_REQUIRED: \"TWO_FACTOR_REQUIRED\",\n /** The action needs a recent sign-in. */\n SESSION_NOT_FRESH: \"SESSION_NOT_FRESH\",\n /** The session has ended. */\n SESSION_EXPIRED: \"SESSION_EXPIRED\",\n /** A settings change would leave no administrator able to use the admin area. */\n ADMIN_LOCKOUT: \"ADMIN_LOCKOUT\",\n /** The runtime auth settings changed since they were loaded (a stale-version conflict). */\n SETTINGS_VERSION_CONFLICT: \"SETTINGS_VERSION_CONFLICT\",\n VALIDATION_FAILED: \"VALIDATION_FAILED\",\n RATE_LIMITED: \"RATE_LIMITED\",\n UNAUTHORIZED: \"UNAUTHORIZED\",\n FORBIDDEN: \"FORBIDDEN\",\n NOT_FOUND: \"NOT_FOUND\",\n INTERNAL_ERROR: \"INTERNAL_ERROR\",\n /** A failed response without a recognisable code. */\n REQUEST_FAILED: \"REQUEST_FAILED\",\n} as const;\n\n/**\n * What a route asks of the session before it can be used: complete 2FA, or\n * sign in again with one of `allowedMethods`.\n */\nexport type AccessRequirement = { kind: \"two-factor\" } | { kind: \"step-up\"; allowedMethods: string[] };\n\n// For bodies without a code, as `@awsmbiz/inbis-api` names them.\nconst HTTP_CODES: Partial<Record<number, string>> = {\n 400: \"BAD_REQUEST\",\n 401: \"UNAUTHORIZED\",\n 403: \"FORBIDDEN\",\n 404: \"NOT_FOUND\",\n 405: \"METHOD_NOT_ALLOWED\",\n 409: \"CONFLICT\",\n 413: \"PAYLOAD_TOO_LARGE\",\n 415: \"UNSUPPORTED_MEDIA_TYPE\",\n 422: \"VALIDATION_FAILED\",\n 429: \"RATE_LIMITED\",\n 500: \"INTERNAL_ERROR\",\n};\n\nconst API_ERROR = Symbol.for(\"@awsmbiz/inbis-api-client/ApiError\");\n\nexport interface ApiErrorOptions {\n status: number;\n code: string;\n message: string;\n requestId?: string | null;\n details?: unknown;\n cause?: unknown;\n}\n\n/** A failed call. `code` is stable and machine-readable; `message` is safe to show. */\nexport class ApiError extends Error {\n /** HTTP status; `0` when no response arrived (`NETWORK_ERROR`). */\n readonly status: number;\n readonly code: string;\n /** The server's request ID (app routes), for support and logs. */\n readonly requestId: string | null;\n /** Extra caller-safe information from the server, e.g. `{ allowedMethods }` or validation issues. */\n readonly details: unknown;\n readonly [API_ERROR] = true;\n\n constructor(options: ApiErrorOptions) {\n super(options.message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"ApiError\";\n this.status = options.status;\n this.code = options.code;\n this.requestId = options.requestId ?? null;\n this.details = options.details;\n }\n\n /** The server could not be reached. */\n get isNetworkError(): boolean {\n return this.code === API_ERROR_CODES.NETWORK_ERROR;\n }\n\n /**\n * A stale-version conflict: someone else changed the resource since it was\n * loaded (`409` with a `…VERSION_CONFLICT` code, e.g. `SETTINGS_VERSION_CONFLICT`).\n * Reload and let the user reapply their change.\n */\n get isVersionConflict(): boolean {\n return this.status === 409 && this.code.endsWith(\"VERSION_CONFLICT\");\n }\n\n /** What the session needs before retrying (`TWO_FACTOR_REQUIRED` / `STEP_UP_REQUIRED`), else `null`. */\n get requirement(): AccessRequirement | null {\n if (this.code === API_ERROR_CODES.TWO_FACTOR_REQUIRED) return { kind: \"two-factor\" };\n if (this.code !== API_ERROR_CODES.STEP_UP_REQUIRED) return null;\n const allowed = (this.details as { allowedMethods?: unknown } | null | undefined)?.allowedMethods;\n return {\n kind: \"step-up\",\n allowedMethods: Array.isArray(allowed) ? allowed.filter((m): m is string => typeof m === \"string\") : [],\n };\n }\n\n /** Reads a failed response's body (either error shape). Consumes the body. */\n static async fromResponse(response: Response): Promise<ApiError> {\n const body: unknown = await response.json().catch(() => null);\n return ApiError.fromBody(response.status, body);\n }\n\n /** Builds the error from a status and a parsed body (either error shape, or anything else). */\n static fromBody(status: number, body: unknown): ApiError {\n const record = asRecord(body);\n const fields = asRecord(record?.[\"error\"]) ?? record;\n const code = fields?.[\"code\"];\n const message = fields?.[\"message\"];\n const requestId = fields?.[\"requestId\"];\n return new ApiError({\n status,\n code: typeof code === \"string\" && code !== \"\" ? code : (HTTP_CODES[status] ?? (status >= 500 ? \"INTERNAL_ERROR\" : \"REQUEST_FAILED\")),\n message: typeof message === \"string\" && message !== \"\" ? message : \"The request failed.\",\n requestId: typeof requestId === \"string\" ? requestId : null,\n details: fields?.[\"details\"],\n });\n }\n\n /** No response arrived. */\n static network(cause?: unknown): ApiError {\n return new ApiError({ status: 0, code: API_ERROR_CODES.NETWORK_ERROR, message: \"Could not reach the server.\", cause });\n }\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) ? (value as Record<string, unknown>) : null;\n}\n\n/** True for any {@link ApiError}, including instances from another copy of this package. */\nexport function isApiError(value: unknown): value is ApiError {\n return typeof value === \"object\" && value !== null && API_ERROR in value;\n}\n","import createClient, { type Client } from \"openapi-fetch\";\nimport { ApiError, isApiError } from \"./api-error.js\";\n\nexport interface ApiClientOptions {\n /** Origin of the API (e.g. `https://app.example.com`). Defaults to the page's own origin. */\n baseUrl?: string;\n /** Custom `fetch` (tests, SSR). */\n fetch?: (request: Request) => Promise<Response>;\n /** Headers sent with every call. */\n headers?: Record<string, string>;\n}\n\n/**\n * A typed client for an app's routes; `Paths` is the `paths` type\n * `openapi-typescript` generates. Failed calls reject with an {@link ApiError}\n * (so `data` is set whenever a call resolves).\n */\nexport type ApiClient<Paths extends object> = Client<Paths>;\n\n/** Creates the typed client for the app's routes (see the README for generating `Paths`). */\nexport function createApiClient<Paths extends object>(options: ApiClientOptions = {}): ApiClient<Paths> {\n const client = createClient<Paths>({\n baseUrl: options.baseUrl ?? \"\",\n // Session cookies on cross-origin calls to a trusted API origin.\n credentials: \"include\",\n ...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n ...(options.headers === undefined ? {} : { headers: options.headers }),\n });\n client.use({\n onResponse: async ({ response }) => {\n if (!response.ok) throw await ApiError.fromResponse(response.clone());\n return undefined;\n },\n onError: ({ error }) => (isApiError(error) ? error : ApiError.network(error)),\n });\n return client;\n}\n","/**\n * `@awsmbiz/inbis-access-control`'s role admin API (`mountRoleAdmin`, under\n * `/api/admin`): the permission catalogue, roles, and users' role\n * assignments. Errors use the `@awsmbiz/inbis-api` envelope; every call rejects with\n * an {@link ApiError}.\n */\nimport { ApiError } from \"./api-error.js\";\n\n/** A permission declared in the app's code (`GET /api/admin/permissions`). */\nexport interface AdminPermission {\n /** Dotted name, e.g. `notes.read`. */\n name: string;\n description: string;\n}\n\n/** A role (`GET /api/admin/roles`). */\nexport interface AdminRole {\n id: string;\n name: string;\n description: string | null;\n /** Declared in code: read-only (`409 SYSTEM_ROLE_IMMUTABLE`). */\n system: boolean;\n /** Permission names the role grants itself. */\n permissions: string[];\n /** IDs of the roles whose permissions this role also grants. */\n inherits: string[];\n /** Users assigned this role directly. */\n userCount: number;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/** A custom role's fields: all of them to create one, any of them to change one. */\nexport interface AdminRoleInput {\n /** 1–64 characters, unique among roles ignoring case. */\n name: string;\n /** Up to 500 characters; `null` or `\"\"` for none. */\n description?: string | null;\n permissions: readonly string[];\n inherits?: readonly string[];\n}\n\n/** A role a user holds directly (`GET /api/admin/users/:userId/roles`). */\nexport interface AdminUserRole {\n id: string;\n name: string;\n system: boolean;\n assignedAt: Date;\n /** The admin who assigned it, when known. */\n assignedBy: string | null;\n}\n\ninterface Init { signal?: AbortSignal }\n\n/** Role admin calls. Reads need the admin policy; changes also a recent sign-in (`403 SESSION_NOT_FRESH`). */\nexport interface RoleAdminClient {\n permissions: {\n list(init?: Init): Promise<{ items: AdminPermission[] }>;\n };\n roles: {\n /** System roles first, then by name. */\n list(init?: Init): Promise<{ items: AdminRole[] }>;\n /** `404 ROLE_NOT_FOUND`. */\n get(roleId: string, init?: Init): Promise<AdminRole>;\n /**\n * Refusals: `422 VALIDATION_FAILED` (`details.issues[{ path, message }]`), `422 UNKNOWN_PERMISSION`,\n * `422 UNKNOWN_ROLE`, `422 ROLE_INHERITANCE_CYCLE`, `409 ROLE_NAME_TAKEN`.\n */\n create(input: AdminRoleInput): Promise<AdminRole>;\n /** As `create`, plus `409 SYSTEM_ROLE_IMMUTABLE` and `404 ROLE_NOT_FOUND`. */\n update(roleId: string, changes: Partial<AdminRoleInput>): Promise<AdminRole>;\n /** Its assignments go with it. `409 ROLE_INHERITED` while another role inherits it, `409 SYSTEM_ROLE_IMMUTABLE`. */\n delete(roleId: string): Promise<void>;\n };\n /** A user's directly assigned roles. Assigning and removing are idempotent and return the user's roles. */\n userRoles: {\n list(userId: string, init?: Init): Promise<{ items: AdminUserRole[] }>;\n /** `404 ROLE_NOT_FOUND` / `404 USER_NOT_FOUND`. */\n assign(userId: string, roleId: string): Promise<{ items: AdminUserRole[] }>;\n unassign(userId: string, roleId: string): Promise<{ items: AdminUserRole[] }>;\n };\n}\n\ntype Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\ntype WireRole = Omit<AdminRole, \"createdAt\" | \"updatedAt\"> & { createdAt: string; updatedAt: string };\ninterface WireUserRoles { items: (Omit<AdminUserRole, \"assignedAt\"> & { assignedAt: string })[] }\n\nconst reviveRole = (role: WireRole): AdminRole => ({ ...role, createdAt: new Date(role.createdAt), updatedAt: new Date(role.updatedAt) });\nconst reviveUserRoles = (body: WireUserRoles) => ({ items: body.items.map((role) => ({ ...role, assignedAt: new Date(role.assignedAt) })) });\n\n/** The role admin calls under `adminBase` (origin + admin path, e.g. `https://app.test/api/admin`). */\nexport function createRoleAdminClient(adminBase: string, doFetch: Fetch): RoleAdminClient {\n const call = async (path: string, init: { method?: string; body?: unknown; signal?: AbortSignal | undefined } = {}): Promise<Response> => {\n let response: Response;\n try {\n response = await doFetch(`${adminBase}${path}`, {\n method: init.method ?? \"GET\",\n credentials: \"include\",\n headers: init.body === undefined ? { accept: \"application/json\" } : { accept: \"application/json\", \"content-type\": \"application/json\" },\n ...(init.body === undefined ? {} : { body: JSON.stringify(init.body) }),\n ...(init.signal === undefined ? {} : { signal: init.signal }),\n });\n } catch (cause) {\n throw ApiError.network(cause);\n }\n if (!response.ok) throw await ApiError.fromResponse(response);\n return response;\n };\n const json = async <T>(path: string, init?: Parameters<typeof call>[1]): Promise<T> => (await (await call(path, init)).json()) as T;\n const role = (roleId: string) => `/roles/${encodeURIComponent(roleId)}`;\n const userRole = (userId: string, roleId: string) => `/users/${encodeURIComponent(userId)}/roles/${encodeURIComponent(roleId)}`;\n return {\n permissions: { list: (init = {}) => json(\"/permissions\", { signal: init.signal }) },\n roles: {\n list: async (init = {}) => ({ items: (await json<{ items: WireRole[] }>(\"/roles\", { signal: init.signal })).items.map(reviveRole) }),\n get: async (roleId, init = {}) => reviveRole(await json<WireRole>(role(roleId), { signal: init.signal })),\n create: async (input) => reviveRole(await json<WireRole>(\"/roles\", { method: \"POST\", body: input })),\n update: async (roleId, changes) => reviveRole(await json<WireRole>(role(roleId), { method: \"PATCH\", body: changes })),\n delete: async (roleId) => {\n await call(role(roleId), { method: \"DELETE\" });\n },\n },\n userRoles: {\n list: async (userId, init = {}) => reviveUserRoles(await json(`/users/${encodeURIComponent(userId)}/roles`, { signal: init.signal })),\n assign: async (userId, roleId) => reviveUserRoles(await json(userRole(userId, roleId), { method: \"PUT\" })),\n unassign: async (userId, roleId) => reviveUserRoles(await json(userRole(userId, roleId), { method: \"DELETE\" })),\n },\n };\n}\n","/**\n * The platform's admin app routes (Hono, under `/api/admin`): the dashboard\n * stats (`mountAuthStats` from `@awsmbiz/inbis-auth/hono`), the audit timeline\n * (`mountAuditTimeline` from `@awsmbiz/inbis-audit/hono`), and roles (`role-admin.ts`). Their errors use the\n * `@awsmbiz/inbis-api` envelope; all reject with an {@link ApiError}.\n */\nimport { ApiError } from \"./api-error.js\";\nimport { createRoleAdminClient, type RoleAdminClient } from \"./role-admin.js\";\n\n/** The admin dashboard's counts (`GET /api/admin/auth-stats`). */\nexport interface AuthAdminStats {\n totalUsers: number;\n /** Users active in the last 7 days (signed in, per the audit trail; or with a session used, without one). */\n activeUsers7d: number;\n /** Invitations neither accepted, revoked nor expired. */\n pendingInvitations: number;\n /** Failed sign-ins in the last 24 hours. */\n failedSignIns24h: number;\n generatedAt: Date;\n}\n\n/** One audit event (`GET /api/admin/audit-events`). */\nexport interface AuditEvent {\n id: string;\n /** Dotted action name, e.g. `auth.sign_in.failed`. */\n action: string;\n actorId: string | null;\n /** e.g. `user:<id>`. */\n subjectId: string | null;\n occurredAt: Date;\n requestId: string | null;\n metadata: Record<string, unknown> | null;\n}\n\n/** Audit timeline filters and paging. `from` is inclusive, `to` exclusive. */\nexport interface AuditEventsQuery {\n action?: string;\n /** User ID of the actor. */\n actor?: string;\n /** e.g. `user:<id>`. */\n subject?: string;\n from?: Date;\n to?: Date;\n /** 1–200, default 50. */\n limit?: number;\n cursor?: string;\n}\n\n/** A user as the admin directory lists them (`GET /api/admin/users`). */\nexport interface AdminUserSummary {\n id: string;\n name: string;\n email: string;\n /** `email` is a phone-only user's placeholder, not an address: never show it as one. */\n emailIsPlaceholder: boolean;\n emailVerified: boolean;\n phoneNumber: string | null;\n role: \"admin\" | \"user\";\n /** Banned now (an expired ban reads `false`). */\n banned: boolean;\n banReason: string | null;\n /** When the ban ends; `null` for a permanent ban or no ban. */\n banExpires: Date | null;\n twoFactorEnabled: boolean;\n createdAt: Date;\n}\n\n/** A way a user can sign in. */\nexport interface AdminUserSignInMethod {\n kind: \"password\" | \"phone\" | \"passkey\" | \"social\";\n /** `credential`, `phone-number`, `passkey`, or the OAuth provider's ID. */\n providerId: string;\n /** A passkey's name or the phone number, when there is one. */\n label: string | null;\n createdAt: Date | null;\n}\n\n/** One user in full (`GET /api/admin/users/:userId`). */\nexport interface AdminUserDetail extends AdminUserSummary {\n phoneNumberVerified: boolean;\n signInMethods: AdminUserSignInMethod[];\n updatedAt: Date;\n}\n\n/** The directory's search, filters (all apply at once), sort and offset paging. */\nexport interface AdminUsersQuery {\n /** Name, email or phone number contains this, ignoring case. */\n search?: string;\n role?: \"admin\" | \"user\";\n banned?: boolean;\n twoFactor?: boolean;\n emailVerified?: boolean;\n /** Default `createdAt` (newest first). */\n sort?: \"createdAt\" | \"name\" | \"email\";\n direction?: \"asc\" | \"desc\";\n /** 1–100, default 25. */\n limit?: number;\n offset?: number;\n}\n\nexport interface AdminUsersPage {\n users: AdminUserSummary[];\n total: number;\n limit: number;\n offset: number;\n}\n\n/** The admin routes, including the role admin API (`mountRoleAdmin` from `@awsmbiz/inbis-access-control/hono`). */\nexport interface AdminRoutesClient extends RoleAdminClient {\n /** The dashboard counts. Administrators only. */\n stats(init?: { signal?: AbortSignal }): Promise<AuthAdminStats>;\n auditEvents: {\n /** Newest first. Pass the previous page's `nextCursor` as `cursor`, with the same filters. */\n list(query?: AuditEventsQuery, init?: { signal?: AbortSignal }): Promise<{ items: AuditEvent[]; nextCursor: string | null }>;\n };\n /** The user directory (`mountAuthUsers` from `@awsmbiz/inbis-auth/hono`). Administrators only. */\n users: {\n list(query?: AdminUsersQuery, init?: { signal?: AbortSignal }): Promise<AdminUsersPage>;\n /** `404 USER_NOT_FOUND` for an unknown ID. */\n get(userId: string, init?: { signal?: AbortSignal }): Promise<AdminUserDetail>;\n /**\n * Removes the user's authenticator and backup codes and signs them out\n * everywhere (lost authenticator); audited. Needs a recent sign-in\n * (`403 SESSION_NOT_FRESH`: wrap it in `withStepUp`); `400 CANNOT_RESET_OWN_TWO_FACTOR`\n * for the caller's own account; `404 USER_NOT_FOUND`.\n */\n resetTwoFactor(userId: string): Promise<{ sessionsRevoked: number }>;\n };\n}\n\n// Dates as JSON carries them.\ntype Wire<T> = { [K in keyof T]: T[K] extends Date ? string : T[K] extends Date | null ? string | null : T[K] };\ntype WireDetail = Wire<Omit<AdminUserDetail, \"signInMethods\">> & { signInMethods: Wire<AdminUserSignInMethod>[] };\n\nconst dateOrNull = (value: string | null) => (value === null ? null : new Date(value));\nconst reviveSummary = <T extends Wire<AdminUserSummary>>(user: T) => ({\n ...user,\n banExpires: dateOrNull(user.banExpires),\n createdAt: new Date(user.createdAt),\n});\n\ntype Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\nfunction searchOf(query: AuditEventsQuery | AdminUsersQuery): string {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query) as [string, string | number | boolean | Date | undefined][]) {\n if (value === undefined) continue;\n params.set(key, value instanceof Date ? value.toISOString() : String(value));\n }\n const search = params.toString();\n return search === \"\" ? \"\" : `?${search}`;\n}\n\nexport function createAdminRoutesClient(options: { baseURL: string; adminPath: string; fetch: Fetch }): AdminRoutesClient {\n const request = async <T>(method: \"GET\" | \"POST\", path: string, signal?: AbortSignal): Promise<T> => {\n let response: Response;\n try {\n response = await options.fetch(`${options.baseURL}${options.adminPath}${path}`, {\n method,\n credentials: \"include\",\n headers: method === \"POST\" ? { accept: \"application/json\", \"content-type\": \"application/json\" } : { accept: \"application/json\" },\n ...(method === \"POST\" ? { body: \"{}\" } : {}),\n ...(signal === undefined ? {} : { signal }),\n });\n } catch (cause) {\n throw ApiError.network(cause);\n }\n if (!response.ok) throw await ApiError.fromResponse(response);\n return (await response.json()) as T;\n };\n const get = <T>(path: string, signal: AbortSignal | undefined) => request<T>(\"GET\", path, signal);\n return {\n ...createRoleAdminClient(`${options.baseURL}${options.adminPath}`, options.fetch),\n stats: async (init = {}) => {\n const body = await get<Omit<AuthAdminStats, \"generatedAt\"> & { generatedAt: string }>(\"/auth-stats\", init.signal);\n return { ...body, generatedAt: new Date(body.generatedAt) };\n },\n auditEvents: {\n list: async (query = {}, init = {}) => {\n const body = await get<{ items: (Omit<AuditEvent, \"occurredAt\"> & { occurredAt: string })[]; nextCursor: string | null }>(\n `/audit-events${searchOf(query)}`,\n init.signal,\n );\n return { items: body.items.map((item) => ({ ...item, occurredAt: new Date(item.occurredAt) })), nextCursor: body.nextCursor };\n },\n },\n users: {\n list: async (query = {}, init = {}) => {\n const body = await get<Omit<AdminUsersPage, \"users\"> & { users: Wire<AdminUserSummary>[] }>(`/users${searchOf(query)}`, init.signal);\n return { ...body, users: body.users.map(reviveSummary) };\n },\n get: async (userId, init = {}) => {\n const { user } = await get<{ user: WireDetail }>(`/users/${encodeURIComponent(userId)}`, init.signal);\n return {\n ...reviveSummary(user),\n signInMethods: user.signInMethods.map((method) => ({ ...method, createdAt: dateOrNull(method.createdAt) })),\n updatedAt: new Date(user.updatedAt),\n };\n },\n resetTwoFactor: (userId) => request(\"POST\", `/users/${encodeURIComponent(userId)}/two-factor/reset`),\n },\n };\n}\n","/**\n * `@awsmbiz/inbis-auth`'s runtime settings routes (`mountAuthSettings`): the wire\n * shapes and the calls. App routes, so errors use the platform envelope.\n */\nimport { ApiError } from \"./api-error.js\";\n\nexport type RegistrationMode = \"open\" | \"invite-only\" | \"admin-only\" | \"closed\";\nexport type AuthMethodName = \"password\" | \"phone-otp\";\nexport type BreachedPasswordCheck = \"fail-open\" | \"off\";\nexport interface RateLimitRule {\n /** Seconds. */\n window: number;\n max: number;\n}\nexport type RateLimitKey = \"default\" | \"signIn\" | \"passwordReset\" | \"otp\" | \"twoFactor\";\n\n/** Resolved, secret-free settings (`effective` and `appConfig` of {@link AuthSettingsView}). Durations in seconds. */\nexport interface AuthSettingsSnapshot {\n registration: { mode: RegistrationMode; requireEmailVerification: boolean };\n methods: { emailPassword: boolean; phoneOtp: boolean };\n /** `null` when email + password is off in the app config. `maxLength` is read-only. */\n password: { minLength: number; maxLength: number; breachedCheck: BreachedPasswordCheck } | null;\n policies: {\n admin: { allowedMethods: AuthMethodName[]; twoFactor: \"required\" };\n user: { twoFactor: \"optional\" | \"required\" };\n };\n sessions: {\n admin: { idle: number; max: number; freshAge: number };\n user: { expiresIn: number; freshAge: number; rememberMe: boolean };\n };\n rateLimit: Partial<Record<RateLimitKey, RateLimitRule>>;\n /** `null` when phone OTP is off in the app config. */\n phoneOtp: { perNumberLimit: RateLimitRule; perIpLimit: RateLimitRule; dailySmsCap: number } | null;\n}\n\n/** The stored overrides: they can only narrow the app config. Absent means \"as the app config says\". */\nexport interface RuntimeAuthSettings {\n registration?: { mode?: RegistrationMode; requireEmailVerification?: boolean };\n methods?: { emailPassword?: boolean; phoneOtp?: boolean };\n password?: { minLength?: number; breachedCheck?: BreachedPasswordCheck };\n policies?: { admin?: { allowedMethods?: AuthMethodName[] }; user?: { twoFactor?: \"optional\" | \"required\" } };\n sessions?: {\n admin?: { idle?: number; max?: number; freshAge?: number };\n user?: { expiresIn?: number; freshAge?: number; rememberMe?: boolean };\n };\n rateLimit?: Partial<Record<RateLimitKey, RateLimitRule>>;\n phoneOtp?: { perNumberLimit?: RateLimitRule; perIpLimit?: RateLimitRule; dailySmsCap?: number };\n}\n\n/** A JSON merge patch of {@link RuntimeAuthSettings}: `null` removes an override (back to the app config). */\nexport type AuthSettingsPatch = {\n [K in keyof RuntimeAuthSettings]?: { [F in keyof NonNullable<RuntimeAuthSettings[K]>]?: unknown } | null;\n};\n\n/** `GET /api/admin/auth-settings`. */\nexport interface AuthSettingsView {\n /** Send it back with a change; a stale one is refused with `409 SETTINGS_VERSION_CONFLICT`. */\n version: number;\n updatedAt: string | null;\n updatedBy: string | null;\n overrides: RuntimeAuthSettings;\n /** What applies now. */\n effective: AuthSettingsSnapshot;\n /** The app config: the widest each setting can be. */\n appConfig: AuthSettingsSnapshot;\n}\n\n/** One method or service in `GET /api/admin/auth-providers/status`. Never a secret. */\nexport interface AuthProviderStatus {\n id: string;\n kind: \"method\" | \"service\";\n /** On in the app config (for services: its secret or sender is present). */\n configured: boolean;\n /** On now, after the runtime settings. */\n enabled: boolean;\n}\n\nexport interface AuthSettingsAdminClient {\n get(): Promise<AuthSettingsView>;\n /**\n * Changes the overrides. Refusals: `409 SETTINGS_VERSION_CONFLICT` (`isVersionConflict`),\n * `422 INVALID_SETTINGS` (`details.issues[{ path, code, message }]`), `422 ADMIN_LOCKOUT`\n * (`details.paths` names the settings that cause it), `403 TWO_FACTOR_NOT_ENROLLED`,\n * `403 SESSION_NOT_FRESH`.\n */\n update(input: { version: number; settings: AuthSettingsPatch }): Promise<AuthSettingsView>;\n}\n\nexport interface AuthProvidersAdminClient {\n status(): Promise<{ items: AuthProviderStatus[] }>;\n}\n\ntype Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/** The settings and provider-status calls under `adminBase` (origin + admin path, e.g. `https://app.test/api/admin`). */\nexport function authSettingsClients(adminBase: string, doFetch: Fetch) {\n const call = async <T>(path: string, init: RequestInit = {}): Promise<T> => {\n let response: Response;\n try {\n response = await doFetch(`${adminBase}${path}`, { ...init, credentials: \"include\" });\n } catch (cause) {\n throw ApiError.network(cause);\n }\n if (!response.ok) throw await ApiError.fromResponse(response);\n return (await response.json()) as T;\n };\n const settings: AuthSettingsAdminClient = {\n get: () => call(\"/auth-settings\"),\n update: (input) =>\n call(\"/auth-settings\", {\n method: \"PATCH\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(input),\n }),\n };\n const providers: AuthProvidersAdminClient = { status: () => call(\"/auth-providers/status\") };\n return { settings, providers };\n}\n","import { createAuthClient, type AuthClient } from \"better-auth/client\";\nimport { adminClient, type AdminClientOptions } from \"better-auth/client/plugins\";\nimport { createAdminRoutesClient, type AdminRoutesClient } from \"./admin-routes.js\";\nimport { ApiError } from \"./api-error.js\";\nimport { authSettingsClients, type AuthProvidersAdminClient, type AuthSettingsAdminClient } from \"./auth-settings.js\";\n\n// Same default as `@awsmbiz/inbis-auth`'s `app.basePath`.\nconst DEFAULT_BASE_PATH = \"/api/auth\";\n\nexport interface AuthAdminClientOptions {\n /** Origin of the API. Defaults to the page's own origin. */\n baseURL?: string;\n /** Must match the server's `app.basePath`. Default `/api/auth`. */\n basePath?: string;\n /** Where the app mounts the admin app routes (stats, audit events, auth settings, provider status). Default `/api/admin`. */\n adminPath?: string;\n /** Custom `fetch` (tests, SSR). */\n fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n}\n\n/** Better Auth's browser client with the admin plugin; methods resolve to data or reject with an {@link ApiError}. */\nexport type BetterAuthAdminClient = AuthClient<{\n // Instantiated explicitly: a bare `typeof adminClient` loses the plugin's endpoints.\n plugins: [ReturnType<typeof adminClient<AdminClientOptions>>];\n fetchOptions: { throw: true };\n}>;\n\nfunction pageOrigin(): string {\n const location = (globalThis as { location?: { origin?: string } }).location;\n return location?.origin ?? \"\";\n}\n\nfunction betterAuthClient(options: AuthAdminClientOptions): BetterAuthAdminClient {\n const doFetch = options.fetch ?? ((input, init) => fetch(input, init));\n return createAuthClient({\n // Better Auth needs an absolute URL: default to the page's own origin.\n baseURL: `${options.baseURL ?? pageOrigin()}${options.basePath ?? DEFAULT_BASE_PATH}`,\n plugins: [adminClient()],\n fetchOptions: {\n // Methods resolve to their data; failures reject with an ApiError (thrown below).\n throw: true,\n credentials: \"include\",\n customFetchImpl: async (input, init) => {\n try {\n return await doFetch(input, init);\n } catch (cause) {\n throw ApiError.network(cause);\n }\n },\n onError: ({ response, error }) => {\n throw ApiError.fromBody(response.status, error);\n },\n },\n });\n}\n\nexport type InvitationStatus = \"pending\" | \"accepted\" | \"revoked\" | \"expired\";\n\n/** An invitation as the admin API returns it (never its token). Dates are revived by Better Auth's client. */\nexport interface Invitation {\n id: string;\n kind: string;\n /** Exactly one of `email` and `phoneNumber` is set. */\n email: string | null;\n phoneNumber: string | null;\n role: \"admin\" | \"user\";\n status: InvitationStatus;\n expiresAt: Date;\n createdAt: Date;\n invitedBy: string | null;\n acceptedAt: Date | null;\n acceptedUserId: string | null;\n revokedAt: Date | null;\n}\n\n/** `@awsmbiz/inbis-auth`'s invitation admin endpoints. Refusals reject with an {@link ApiError}. */\nexport interface InvitationsAdminClient {\n /**\n * Invites by email or by phone (E.164), as `role` (default `user`). Errors include\n * `INVALID_EMAIL`, `INVALID_PHONE_NUMBER`, `USER_ALREADY_EXISTS` and `INVITATION_ALREADY_PENDING`.\n */\n create(input: { email: string; role?: \"admin\" | \"user\" } | { phoneNumber: string; role?: \"admin\" | \"user\" }): Promise<{\n invitation: Invitation;\n }>;\n /**\n * Newest first. Pass the previous page's `nextCursor` as `cursor`, and `status` for only\n * invitations with that status (`INVALID_INVITATION_STATUS` for anything else).\n */\n list(input?: { limit?: number; cursor?: string; status?: InvitationStatus }): Promise<{ items: Invitation[]; nextCursor: string | null }>;\n /** Sends a fresh link; the expiry restarts. `INVITATION_NOT_PENDING` once accepted or revoked. */\n resend(input: { invitationId: string }): Promise<{ invitation: Invitation }>;\n /** `INVITATION_NOT_PENDING` once accepted or revoked. */\n revoke(input: { invitationId: string }): Promise<{ invitation: Invitation }>;\n}\n\n/** Who a new user is: an email address, or a phone number (E.164; a phone-only user signs in with codes). */\nexport type CreateUserInput = {\n name: string;\n role?: \"admin\" | \"user\";\n /** Email users only. Without one, the user sets a password with \"Forgot password\". */\n password?: string;\n} & ({ email: string; phoneNumber?: never } | { phoneNumber: string; email?: never });\n\n/** A created user, as Better Auth's admin plugin returns it (dates revived). */\nexport interface CreatedUser {\n id: string;\n name: string;\n email: string;\n role?: string | null;\n createdAt: Date;\n}\n\n/**\n * Admin user actions that `@awsmbiz/inbis-auth` adds to Better Auth's admin plugin.\n * The rest (set role, ban, unban, revoke every session, remove) are on\n * `admin`. Refusals reject with an {@link ApiError}.\n */\nexport interface UserActionsAdminClient {\n /**\n * Creates a user by email or by phone number. Errors include\n * `USER_ALREADY_EXISTS`, `INVALID_PHONE_NUMBER`, `PHONE_COUNTRY_NOT_ALLOWED`\n * and `PHONE_SIGN_IN_UNAVAILABLE`.\n */\n create(input: CreateUserInput): Promise<{ user: CreatedUser }>;\n /**\n * Signs a user out of one session, by the session's ID (the listing never\n * carries session tokens). `404 SESSION_NOT_FOUND` once it has ended.\n */\n revokeSession(input: { userId: string; sessionId: string }): Promise<{ success: boolean }>;\n}\n\n/**\n * Auth admin calls: Better Auth's admin plugin, `@awsmbiz/inbis-auth`'s invitation\n * endpoints, and the admin app routes (`stats`, `auditEvents`).\n */\nexport interface AuthAdminClient extends AdminRoutesClient {\n /** Better Auth's admin plugin (`listUsers`, `createUser`, `setRole`, `banUser`, `unbanUser`, …). */\n admin: BetterAuthAdminClient[\"admin\"];\n invitations: InvitationsAdminClient;\n /** User actions beyond Better Auth's: create by phone number, revoke a session by ID. */\n userActions: UserActionsAdminClient;\n /** Runtime auth settings (`GET`/`PATCH /api/admin/auth-settings`). */\n settings: AuthSettingsAdminClient;\n /** Method and provider status (`GET /api/admin/auth-providers/status`). */\n providers: AuthProvidersAdminClient;\n}\n\n/** Creates the client for auth admin endpoints. */\nexport function createAuthAdminClient(options: AuthAdminClientOptions = {}): AuthAdminClient {\n const client = betterAuthClient(options);\n // The client's `throw: true` makes `$fetch` resolve to the parsed body (its\n // types don't follow the option) and reject with an ApiError.\n const call = client.$fetch as unknown as <T>(\n path: string,\n init: { method: \"GET\"; query: object } | { method: \"POST\"; body: object },\n ) => Promise<T>;\n return {\n ...createAdminRoutesClient({\n baseURL: options.baseURL ?? pageOrigin(),\n adminPath: options.adminPath ?? \"/api/admin\",\n fetch: options.fetch ?? ((input, init) => fetch(input, init)),\n }),\n admin: client.admin,\n userActions: {\n create: (input) => call(\"/admin/create-user\", { method: \"POST\", body: input }),\n revokeSession: (input) => call(\"/admin/revoke-user-session-by-id\", { method: \"POST\", body: input }),\n },\n invitations: {\n create: (input) => call(\"/admin/create-invitation\", { method: \"POST\", body: input }),\n list: (input = {}) => call(\"/admin/list-invitations\", { method: \"GET\", query: input }),\n resend: (input) => call(\"/admin/resend-invitation\", { method: \"POST\", body: input }),\n revoke: (input) => call(\"/admin/revoke-invitation\", { method: \"POST\", body: input }),\n },\n ...authSettingsClients(\n `${options.baseURL ?? pageOrigin()}${options.adminPath ?? \"/api/admin\"}`,\n options.fetch ?? ((input, init) => fetch(input, init)),\n ),\n };\n}\n","/**\n * TanStack Query helpers. Framework-agnostic: they return plain\n * `{ queryKey, queryFn }` objects for `useQuery`/`fetchQuery`, and take any\n * object with `invalidateQueries` (a `QueryClient`).\n *\n * Key conventions:\n * - app routes: `[\"api\", path, params?]`, e.g. `[\"api\", \"/v1/notes/{id}\", { path: { id } }]`;\n * - auth admin: `[\"auth-admin\", \"users\" | \"invitations\" | \"stats\" | \"audit-events\" | \"roles\" | \"permissions\", …]` ({@link authAdminKeys}).\n */\nimport type { FetchOptions, FetchResponse } from \"openapi-fetch\";\nimport type { MediaType, PathsWithMethod } from \"openapi-typescript-helpers\";\nimport type { AdminUsersQuery, AuditEventsQuery } from \"./admin-routes.js\";\nimport type { ApiClient } from \"./api-client.js\";\nimport type { AuthAdminClient, Invitation, InvitationStatus } from \"./auth-admin-client.js\";\n\n/** Anything that can invalidate queries by key prefix (TanStack's `QueryClient`). */\nexport interface QueryInvalidator {\n invalidateQueries(filters: { queryKey: readonly unknown[] }): Promise<unknown>;\n}\n\n/** Invalidates every query whose key starts with one of `keys`. */\nexport async function invalidateQueries(queryClient: QueryInvalidator, ...keys: (readonly unknown[])[]): Promise<void> {\n await Promise.all(keys.map((queryKey) => queryClient.invalidateQueries({ queryKey })));\n}\n\n// ---------------------------------------------------------------------------\n// App routes\n\n/** The key of an app-route query: `[\"api\", path]`, or `[\"api\", path, params]`. */\nexport function apiQueryKey<Path extends string>(path: Path): readonly [\"api\", Path];\nexport function apiQueryKey<Path extends string, Params>(path: Path, params: Params): readonly [\"api\", Path, Params];\nexport function apiQueryKey(path: string, params?: unknown): readonly unknown[] {\n return params === undefined ? [\"api\", path] : [\"api\", path, params];\n}\n\n/** Invalidates every query of the given app-route paths (any params), or of all app routes when none is given. */\nexport function invalidateApi(queryClient: QueryInvalidator, ...paths: string[]): Promise<void> {\n return invalidateQueries(queryClient, ...(paths.length === 0 ? [[\"api\"]] : paths.map((path) => apiQueryKey(path))));\n}\n\n// The GET operation of a path, its request options and its success body.\ntype GetOperation<P, Path> = Path extends keyof P\n ? P[Path] extends { get: infer Operation extends Record<string | number, unknown> }\n ? Operation\n : never\n : never;\ntype GetInit<P, Path> = FetchOptions<GetOperation<P, Path>>;\ntype GetData<P, Path, Init> = NonNullable<FetchResponse<GetOperation<P, Path>, Init, MediaType>[\"data\"]>;\n// As openapi-fetch's (unexported) `InitParam`: optional exactly when nothing in it is required.\ntype RequiredKeysOf<T> = { [K in keyof T]-?: object extends Pick<T, K> ? never : K }[keyof T];\ntype InitParam<Init> = RequiredKeysOf<Init> extends never ? [Init?] : [Init];\n\nexport interface ApiQueryOptions<Key extends readonly unknown[], Data> {\n queryKey: Key;\n queryFn: (context: { signal: AbortSignal }) => Promise<Data>;\n}\n\n/**\n * Query options for a `GET` app route: keyed by path and `params` (see\n * {@link apiQueryKey}), failing with an `ApiError`:\n * `useQuery(apiQueryOptions(api, \"/v1/notes/{id}\", { params: { path: { id } } }))`.\n */\nexport function apiQueryOptions<\n P extends object,\n Path extends PathsWithMethod<P, \"get\"> & string,\n Init extends GetInit<P, Path>,\n>(\n client: ApiClient<P>,\n path: Path,\n ...[init]: InitParam<Init>\n): ApiQueryOptions<readonly [\"api\", Path] | readonly [\"api\", Path, unknown], GetData<P, Path, Init>> {\n const params = (init as { params?: unknown } | undefined)?.params;\n // Failed calls reject (see createApiClient), so `data` is set whenever this resolves.\n const get = client.GET as unknown as (path: string, init: object) => Promise<{ data: GetData<P, Path, Init> }>;\n return {\n queryKey: params === undefined ? apiQueryKey(path) : apiQueryKey(path, params),\n queryFn: async ({ signal }) => (await get(path, { ...init, signal })).data,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Auth admin\n\ntype Admin = AuthAdminClient[\"admin\"];\n/** Better Auth's `listUsers` query (search, filter, sort, `limit`/`offset`). */\nexport type ListUsersQuery = NonNullable<Parameters<Admin[\"listUsers\"]>[0]>[\"query\"];\nexport interface ListInvitationsQuery {\n limit?: number;\n cursor?: string;\n status?: InvitationStatus;\n}\n\n/**\n * Query keys for auth admin data. Invalidate `users()` after any change to a\n * user (it covers lists, details and sessions), `invitations()` after any\n * invitation change, `stats()` after anything the dashboard counts, or `all`\n * for everything.\n */\nexport const authAdminKeys = {\n all: [\"auth-admin\"] as const,\n users: () => [\"auth-admin\", \"users\"] as const,\n userList: (query: ListUsersQuery) => [\"auth-admin\", \"users\", \"list\", query] as const,\n user: (userId: string) => [\"auth-admin\", \"users\", \"detail\", userId] as const,\n userSessions: (userId: string) => [\"auth-admin\", \"users\", \"detail\", userId, \"sessions\"] as const,\n /** The admin directory (`users.list`: combined filters). */\n userDirectory: (query: AdminUsersQuery) => [\"auth-admin\", \"users\", \"directory\", query] as const,\n /** One user's profile, sign-in methods and 2FA status (`users.get`). */\n userProfile: (userId: string) => [\"auth-admin\", \"users\", \"detail\", userId, \"profile\"] as const,\n invitations: () => [\"auth-admin\", \"invitations\"] as const,\n invitationList: (query: ListInvitationsQuery) => [\"auth-admin\", \"invitations\", \"list\", query] as const,\n settings: () => [\"auth-admin\", \"settings\"] as const,\n providerStatus: () => [\"auth-admin\", \"providers\", \"status\"] as const,\n stats: () => [\"auth-admin\", \"stats\"] as const,\n auditEvents: () => [\"auth-admin\", \"audit-events\"] as const,\n auditEventList: (query: AuditEventsQuery) => [\"auth-admin\", \"audit-events\", \"list\", query] as const,\n /** The declared permission catalogue (`permissions.list`). */\n permissions: () => [\"auth-admin\", \"permissions\"] as const,\n /** Every role query: invalidate after a role change or an assignment (it changes `userCount`). */\n roles: () => [\"auth-admin\", \"roles\"] as const,\n roleList: () => [\"auth-admin\", \"roles\", \"list\"] as const,\n role: (roleId: string) => [\"auth-admin\", \"roles\", \"detail\", roleId] as const,\n /** A user's assigned roles (`userRoles.list`); covered by `users()`. */\n userRoles: (userId: string) => [\"auth-admin\", \"users\", \"detail\", userId, \"roles\"] as const,\n};\n\n/** Query options for auth admin data, keyed by {@link authAdminKeys}. */\nexport function authAdminQueries(auth: AuthAdminClient) {\n return {\n userList: (query: ListUsersQuery) => ({\n queryKey: authAdminKeys.userList(query),\n queryFn: ({ signal }: { signal: AbortSignal }) => auth.admin.listUsers({ query, fetchOptions: { signal } }),\n }),\n user: (userId: string) => ({\n queryKey: authAdminKeys.user(userId),\n queryFn: ({ signal }: { signal: AbortSignal }) =>\n auth.admin.getUser({ query: { id: userId }, fetchOptions: { signal } }),\n }),\n userSessions: (userId: string) => ({\n queryKey: authAdminKeys.userSessions(userId),\n queryFn: ({ signal }: { signal: AbortSignal }) =>\n auth.admin.listUserSessions({ userId, fetchOptions: { signal } }),\n }),\n userDirectory: (query: AdminUsersQuery) => ({\n queryKey: authAdminKeys.userDirectory(query),\n queryFn: ({ signal }: { signal: AbortSignal }) => auth.users.list(query, { signal }),\n }),\n userProfile: (userId: string) => ({\n queryKey: authAdminKeys.userProfile(userId),\n queryFn: ({ signal }: { signal: AbortSignal }) => auth.users.get(userId, { signal }),\n }),\n invitationList: (query: ListInvitationsQuery) => ({\n queryKey: authAdminKeys.invitationList(query),\n queryFn: (): Promise<{ items: Invitation[]; nextCursor: string | null }> => auth.invitations.list(query),\n }),\n settings: () => ({ queryKey: authAdminKeys.settings(), queryFn: () => auth.settings.get() }),\n providerStatus: () => ({ queryKey: authAdminKeys.providerStatus(), queryFn: () => auth.providers.status() }),\n stats: () => ({\n queryKey: authAdminKeys.stats(),\n queryFn: ({ signal }: { signal: AbortSignal }) => auth.stats({ signal }),\n }),\n auditEventList: (query: AuditEventsQuery) => ({\n queryKey: authAdminKeys.auditEventList(query),\n queryFn: ({ signal }: { signal: AbortSignal }) => auth.auditEvents.list(query, { signal }),\n }),\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
2
  "name": "@awsmbiz/inbis-api-client",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Typed browser client for Inbis apps: openapi-fetch over the app's OpenAPI document, Better Auth's admin client, one ApiError and TanStack Query helpers.",
5
+ "license": "UNLICENSED",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/awsmbiz/inbis-platform.git",
9
+ "directory": "packages/api-client"
10
+ },
11
+ "publishConfig": {
12
+ "registry": "https://registry.npmjs.org/",
13
+ "access": "public"
14
+ },
15
+ "type": "module",
16
+ "sideEffects": false,
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "./package.json": "./package.json"
26
+ },
27
+ "dependencies": {
28
+ "better-auth": "1.7.14",
29
+ "openapi-fetch": "0.17.0",
30
+ "openapi-typescript-helpers": "0.1.0"
31
+ },
32
+ "devDependencies": {
33
+ "@arethetypeswrong/cli": "0.18.5",
34
+ "@hono/zod-openapi": "1.6.3",
35
+ "@awsmbiz/inbis-api": "0.1.0",
36
+ "@awsmbiz/inbis-core": "0.1.0",
37
+ "@awsmbiz/inbis-eslint-config": "0.1.0",
38
+ "@awsmbiz/inbis-tsconfig": "0.1.0",
39
+ "@tanstack/query-core": "5.104.3",
40
+ "@types/node": "24.19.2",
41
+ "eslint": "10.14.0",
42
+ "hono": "4.13.14",
43
+ "openapi-typescript": "7.13.0",
44
+ "publint": "0.3.25",
45
+ "tsdown": "0.23.4",
46
+ "typescript": "6.0.3",
47
+ "vitest": "5.0.5",
48
+ "zod": "4.6.7"
49
+ },
50
+ "scripts": {
51
+ "build": "tsdown",
52
+ "typecheck": "tsc",
53
+ "lint": "eslint . --max-warnings 0",
54
+ "test": "vitest run",
55
+ "check:exports": "publint --strict && attw --pack . --profile esm-only",
56
+ "generate:fixture-types": "node test/generate-fixture-types.ts"
57
+ }
6
58
  }