Develop or review put.io SDK repositories, API clients, and client libraries across TypeScript, Swift, Kotlin, and similar packages. Use when adding or changing namespaces, tightening request or error types, aligning SDK behavior with backend and app usage, updating SDK verification flows, or checking how an SDK repo should be documented and released.
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Use these examples when a put.io SDK repo needs concrete implementation shape and the repo does not already establish a stronger local pattern.
Keep boundary parsing explicit and keep Promise and Effect surfaces aligned:
const FileSchema = Schema.Struct({
id: Schema.Number,
name: Schema.String,
content_type: Schema.NullOr(Schema.String),
});
export type PutioFile = Schema.Schema.Type<typeof FileSchema>;
export const listFiles = (client: PutioSdkClient, input: ListFilesInput) =>
client.get("/files/list", { query: input }).pipe(
selectJsonField("files"),
Effect.flatMap(Schema.decodeUnknown(Schema.Array(FileSchema))),
withOperationErrors(ListFilesErrorSpec),
);Prefer discriminated unions and parameter-aware types over loose bags:
type SubtitleResult =
| { readonly kind: "available"; readonly subtitles: ReadonlyArray<Subtitle> }
| { readonly kind: "missing" };Avoid unsafe casts, ignored type failures, and ad hoc JSON parsing when Schema already covers the boundary.
Expose ergonomic helpers on top of typed errors instead of forcing every consumer to rebuild the same checks:
export const isAuthError = (error: PutioSdkError): boolean =>
error._tag === "ApiError" && error.code === "invalid_token";
export const getLocalizedError = (error: PutioSdkError): string =>
localizePutioError(error).summary;Model pagination and query-dependent response shapes explicitly:
type PageInput = {
readonly perPage?: number;
readonly cursor?: string;
};
type ListFilesInput<IncludeParent extends boolean = false> = PageInput & {
readonly parentId?: number;
readonly includeParent?: IncludeParent;
};
type ListFilesResult<IncludeParent extends boolean> = {
readonly files: ReadonlyArray<PutioFile>;
readonly cursor: string | null;
} & (IncludeParent extends true
? { readonly parent: PutioFile | null }
: { readonly parent?: never });Model backend state with explicit types and tolerate forward-compatible backend values where needed:
@Serializable
data class PutioFile(
val id: Long,
val name: String,
@SerialName("content_type") val contentType: String? = null,
)
@JvmInline
@Serializable(with = PutioFileTypeSerializer::class)
value class PutioFileType(val raw: String) {
val isKnown: Boolean get() = raw in setOf("VIDEO", "AUDIO", "FOLDER")
}Keep coroutine APIs domain-first and keep request, response, and error models updated together.
Expose helpers around typed exceptions when they materially improve app code:
fun PutioException.isRetryable(): Boolean =
this is PutioException.Transport || this is PutioException.RateLimited
fun PutioException.userMessage(localizer: PutioErrorLocalizer): String =
localizer.localize(this).summaryPrefer explicit pagination and query models over raw maps:
@Serializable
data class PageCursor(
val cursor: String? = null,
@SerialName("per_page") val perPage: Int? = null,
)
@Serializable
data class ListFilesQuery(
@SerialName("parent_id") val parentId: Long? = null,
val cursor: String? = null,
)Keep public contracts explicit and preserve a stable package surface:
public struct AccountInfo: Decodable, Sendable {
public let username: String
public let mail: String
}
public enum PutioSDKError: Error, Sendable {
case transport(TransportError)
case api(status: Int, message: String)
case decoding(DecodingError)
}Use the example app for auth-flow and integration smoke checks when a formal live harness is not available yet.
Expose small adapters around typed errors when apps need them:
extension PutioSDKError: LocalizedError {
public var errorDescription: String? { localizedSummary }
}
extension PutioSDKError {
public var isRetryable: Bool {
switch self {
case .transport: true
default: false
}
}
}Prefer explicit query and pagination types over [String: Any]-style bags:
public struct PageCursor: Sendable, Equatable {
public let cursor: String?
public let perPage: Int?
}
public struct ListFilesQuery: Sendable, Equatable {
public let parentId: Int64?
public let cursor: String?
public let includeParent: Bool
}Healthy SDK repos should keep two layers distinct:
Representative shapes in this workspace:
vp run verify
vp run test:live
./gradlew verify
./gradlew liveTest
make verifyIf a repo only has one layer today, document the gap and prefer adding the missing layer over widening claims about verification quality.