{"id":29536797,"url":"https://github.com/jadengeller/adapterstack","last_synced_at":"2025-07-17T03:09:38.217Z","repository":{"id":302427092,"uuid":"1012253055","full_name":"JadenGeller/AdapterStack","owner":"JadenGeller","description":"Free Abstractions. Explicit Capabilities. Zero Wiring.","archived":false,"fork":false,"pushed_at":"2025-07-02T10:42:58.000Z","size":10,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-02T10:46:13.000Z","etag":null,"topics":["architecture","dependecy-injection","design-patterns","protocol-oriented-programming","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JadenGeller.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-02T04:05:03.000Z","updated_at":"2025-07-02T10:43:01.000Z","dependencies_parsed_at":"2025-07-02T10:46:15.799Z","dependency_job_id":"a6d70b19-37d8-4a1e-9c80-0530ba0d7158","html_url":"https://github.com/JadenGeller/AdapterStack","commit_stats":null,"previous_names":["jadengeller/adapterstack"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/JadenGeller/AdapterStack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JadenGeller%2FAdapterStack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JadenGeller%2FAdapterStack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JadenGeller%2FAdapterStack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JadenGeller%2FAdapterStack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JadenGeller","download_url":"https://codeload.github.com/JadenGeller/AdapterStack/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JadenGeller%2FAdapterStack/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265562382,"owners_count":23788519,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["architecture","dependecy-injection","design-patterns","protocol-oriented-programming","swift"],"created_at":"2025-07-17T03:09:37.473Z","updated_at":"2025-07-17T03:09:38.209Z","avatar_url":"https://github.com/JadenGeller.png","language":"Swift","readme":"# AdapterStack\n\nFree Abstractions. Explicit Capabilities. Zero Wiring.\n\nWrite explicit Swift code. Dependencies are transparent, boundaries are real, and the compiler proves it works.\n\n```swift\n// Zero wiring - one struct provides all your dependencies\nstruct CheckoutServiceProvider: CheckoutServiceAdapterStack {\n    let database = PostgreSQL()\n    let stripe = StripeGateway() \n    let sendgrid = SendGridClient()\n    // Dependencies connect automatically by name\n}\n\n// Explicit capabilities - type signatures show exactly what each function can do\nfunc showCheckout\u003cS: CheckoutService\u003e(service: S) {\n    // service.database  ❌ Compiler error! \n    // Can only use CheckoutService methods\n}\n\nfunc createOrder\u003cS: OrderService\u003e(service: S) {\n    // service.stripe  ❌ Compiler error!\n    // Can only use OrderService methods  \n}\n\nfunc sendReceipts\u003cS: NotificationService\u003e(service: S) {\n    // service.database  ❌ Compiler error!\n    // Can only use NotificationService methods\n}\n\n// Free abstractions - boundaries compile to zero-cost direct calls\nlet provider = CheckoutServiceProvider()\nshowCheckout(service: provider)      // ✅ Same provider\ncreateOrder(service: provider)       // ✅ Different view\nsendReceipts(service: provider)      // ✅ Another view\n```\n\n## The Problem\n\n## The Problem\n\nEvery Swift app needs to manage dependencies between services. You have three choices, and they all hurt:\n\n### Hidden Dependencies\n\nThe convenient path - use singletons everywhere:\n\n```swift\nfunc checkout(cart: Cart) async throws -\u003e Order {\n    let user = AuthManager.shared.currentUser\n    let items = await InventoryAPI.shared.reserve(cart.items)  \n    let payment = try await StripeAPI.shared.charge(cart.total)\n    Analytics.shared.track(\"order_created\")\n    \n    return Order(user: user, items: items, payment: payment)\n}\n```\n\nThis is easy to write but impossible to test, reason about, or refactor. What does `checkout` need? You have to read the entire function body. Want to test with a mock payment provider? Too bad.\n\n### Manual Dependency Passing\n\nThe explicit path - pass everything everywhere:\n\n```swift\nclass CheckoutService {\n    private let orderService: OrderService\n    private let paymentService: PaymentService\n    private let notificationService: NotificationService\n    \n    init(orderService: OrderService, paymentService: PaymentService, \n         notificationService: NotificationService) {\n        self.orderService = orderService\n        self.paymentService = paymentService\n        self.notificationService = notificationService\n    }\n}\n\n// But OrderService has its own dependencies...\nclass OrderService {\n    private let cartService: CartService\n    private let inventoryService: InventoryService\n    private let userService: UserService\n    private let database: Database\n    \n    init(cartService: CartService, inventoryService: InventoryService,\n         userService: UserService, database: Database) {\n        self.cartService = cartService\n        self.inventoryService = inventoryService\n        self.userService = userService\n        self.database = database\n    }\n}\n\n// And those services have dependencies too...\n// Soon you're writing:\nlet db = PostgreSQL()\nlet cartService = CartService(database: db)\nlet inventoryService = InventoryService(database: db, httpClient: client)\nlet userService = UserService(database: db, cache: cache)\nlet orderService = OrderService(\n    cartService: cartService,\n    inventoryService: inventoryService,\n    userService: userService,\n    database: db\n)\nlet checkoutService = CheckoutService(\n    orderService: orderService,\n    paymentService: paymentService,\n    notificationService: notificationService\n)\n// The pain scales with your app\n```\n\nDependencies are explicit and boundaries are real, but the wiring grows without bound. Plus, sharing dependencies requires reference types with heap allocations and reference counting overhead.\n\n### Global Registries\n\nThe middle path - service locators or dependency containers:\n\n```swift\ncontainer.register(Database.self) { PostgreSQL() }\ncontainer.register(PaymentService.self) { StripePayment() }\ncontainer.register(OrderService.self) { \n    OrderService(\n        database: container.resolve(Database.self)!,\n        payment: container.resolve(PaymentService.self)!\n    )\n}\n\nfunc checkout(cart: Cart) async throws -\u003e Order {\n    let orderService = container.resolve(OrderService.self)!\n    let payment = container.resolve(PaymentService.self)!\n    // ...\n}\n```\n\nLess boilerplate, but now you have runtime resolution. Forget to register something? Crash. Typo in your registration? Crash. Want to know what `checkout` needs? Check every `resolve` call. Plus the overhead of runtime lookups instead of direct calls.\n\n## The Solution\n\nAdapterStack gives you free abstractions through Swift's optimizer, explicit capabilities through protocol constraints, and zero wiring through structural sharing:\n\n```swift\n// 1. Define what your service does\nprotocol CheckoutService {\n    func checkout(cart: Cart) async throws -\u003e Order\n}\n\n// 2. Declare what it needs via protocol composition\n@Adapter(CheckoutService.self)  // Generates the CheckoutServiceAdapterStack typealias\nprotocol CheckoutServiceAdapter: CheckoutService, OrderService, PaymentService, NotificationService {}\n\n// 3. Implement by composing other services\nextension CheckoutServiceAdapter {\n    func checkout(cart: Cart) async throws -\u003e Order {\n        let order = try await createOrder(from: cart)        // From OrderService\n        try await processPayment(for: order.total)           // From PaymentService\n        try await sendOrderConfirmation(for: order)          // From NotificationService\n        return order\n    }\n}\n\n// 4. Create one struct with your dependencies\nstruct CheckoutServiceProvider: CheckoutServiceAdapterStack {\n    let database = PostgreSQL()\n    let stripe = StripeGateway()\n    let sendgrid = SendGridClient()\n}\n```\n\nNo wiring. No boilerplate. Just explicit dependencies and compiler-enforced boundaries.\n\n## How It Works\n\n### Adapters Bridge Protocols to Dependencies\n\nThe key insight: separate what a service IS from what it NEEDS.\n\n```swift\n// CartService defines WHAT it does\nprotocol CartService {\n    func loadCart() async throws -\u003e Cart\n    func saveCart(_ cart: Cart) async throws\n}\n\n// CartServiceAdapter declares WHAT IT NEEDS to do that\n@Adapter(CartService.self)\nprotocol CartServiceAdapter: CartService {\n    associatedtype DB: Database\n    var database: DB { get }\n}\n\n// Extension provides HOW using the declared dependencies\nextension CartServiceAdapter {\n    func loadCart() async throws -\u003e Cart {\n        try await database.fetch(\"cart\", as: Cart.self) ?? Cart()\n    }\n    \n    func saveCart(_ cart: Cart) async throws {\n        try await database.save(\"cart\", cart)\n    }\n}\n```\n\nNotice the separation: protocol requirements (like `var database: DB`) declare platform primitives - the foundational capabilities your app needs. Protocol composition (like `: CartService, PaymentService`) is how you build higher-level services.\n\nThe magic happens through Swift's protocol extensions. When an adapter conforms to multiple protocols, it inherits all their extension methods. This is the key Swift feature that makes the pattern work - protocol extensions provide real implementations, not just signatures. Your adapter gets fully-implemented methods from all its composed protocols and can orchestrate them together.\n\n### Stacks Encapsulate Transitive Dependencies\n\nStack saves you from declaring transitive dependencies. When CheckoutService needs OrderService, and OrderService needs CartService, you'd normally need to declare all of them:\n\n```swift\n// The @Adapter macro on each adapter:\n@Adapter(CheckoutService.self)\nprotocol CheckoutServiceAdapter: CheckoutService, OrderService, PaymentService, NotificationService {}\n\n// Generates this typealias:\nextension CheckoutServiceAdapter {\n    typealias CheckoutServiceAdapterStack = CheckoutServiceAdapter \u0026 OrderServiceAdapterStack \u0026 PaymentServiceAdapterStack \u0026 NotificationServiceAdapterStack\n}\n\n// Without Stack: must declare EVERYTHING (gets worse as dependencies grow)\nstruct Provider: CheckoutServiceAdapter, OrderServiceAdapter, PaymentServiceAdapter, \n                 NotificationServiceAdapter, CartServiceAdapter, UserServiceAdapter,\n                 InventoryServiceAdapter, DatabaseAdapter, PaymentGatewayAdapter, \n                 EmailClientAdapter {\n    let database = PostgreSQL()\n    let stripe = StripeGateway()\n    let sendgrid = SendGridClient()\n}\n\n// With Stack: just declare what you directly use\nstruct CheckoutServiceProvider: CheckoutServiceAdapterStack {\n    let database = PostgreSQL()\n    let stripe = StripeGateway()\n    let sendgrid = SendGridClient()\n}\n```\n\nStack gives you local reasoning - you only think about direct dependencies, not the whole tree.\n\n## Core Benefits\n\n### Free Abstractions\n\nTraditional architectures discourage fine-grained services because every boundary requires wiring. This pattern removes both the coding cost AND the runtime cost:\n\n```swift\n// Traditional: One big service (boundaries are expensive)\nclass CheckoutService {\n    func loadCart(...) { }\n    func calculateTax(...) { }\n    func processPayment(...) { }\n    func sendNotification(...) { }\n    // 20+ mixed responsibilities\n}\n\n// This pattern: Many focused services (boundaries are free)\n@Adapter(CartService.self)\nprotocol CartServiceAdapter: CartService, Database {}\n\n@Adapter(TaxService.self)\nprotocol TaxServiceAdapter: TaxService, LocationService {}\n\n@Adapter(PaymentService.self)\nprotocol PaymentServiceAdapter: PaymentService, PaymentGateway {}\n\n@Adapter(CheckoutService.self)\nprotocol CheckoutServiceAdapter: CheckoutService, CartService, TaxService, PaymentService, NotificationService {}\n```\n\nWhen boundaries have no cost, you use them everywhere. Your architecture naturally decomposes into focused, testable units.\n\nThe \"services\" are just protocol methods - no allocations, no vtables, no overhead. Swift's optimizer inlines everything into direct calls, making fine-grained services as fast as monolithic ones.\n\n### Explicit Capabilities\n\nFunctions declare their capabilities in their type signature:\n\n```swift\n// This function can ONLY do cart operations\nfunc updateCart\u003cS: CartService\u003e(service: S, items: [Item]) async throws\n\n// This function can do cart AND payment operations  \nfunc purchaseCart\u003cS: CartService \u0026 PaymentService\u003e(service: S) async throws\n\n// Compare to hidden dependencies:\nfunc purchaseCart() async throws {\n    // What can this function do? No way to know without reading the body\n    let cart = await CartStorage.shared.load()  // Hidden cart access\n    try await StripeAPI.shared.charge(...)      // Hidden payment access\n}\n```\n\nThis is capability-based security at the language level. Functions can only perform the effects you explicitly grant them. Perfect local reasoning about what code can do.\n\nLike effect systems in functional languages, the type signature tells you exactly what effects a function can perform - but using Swift's protocol system rather than specialized syntax.\n\n### Zero Wiring\n\nWhen multiple services need the same dependency, they share it automatically through structural typing:\n\n```swift\nprotocol CartServiceAdapter: CartService {\n    associatedtype DB: Database\n    var database: DB { get }\n}\n\nprotocol OrderServiceAdapter: OrderService {\n    associatedtype DB: Database\n    var database: DB { get }  // Same property name!\n}\n\n// One property satisfies both\nstruct CheckoutServiceProvider: CheckoutServiceAdapterStack {\n    let database = PostgreSQL()  // Shared by ALL services needing 'database'\n    let stripe = StripeGateway()\n    let sendgrid = SendGridClient()\n}\n```\n\nDependencies propagate automatically by name through structural typing. No manual wiring, no framework, no registry. If two services need the same dependency with the same property name, they share it.\n\n## Why This Works\n\nThis pattern achieves something usually impossible: it combines implicit parameter behavior with access control boundaries.\n\nTraditional approaches make you choose:\n- **Manual dependency passing** requires objects with lifecycles, retain graphs, and explicit wiring. To share dependencies between services, you need reference types scattered across the heap.\n- **Global registries** (service locators, dependency containers) give you convenience but no boundaries - any code can grab any dependency\n- **Singletons** are convenient but create hidden dependencies and rigid coupling\n\nAdapterStack uses a single struct (not objects!) that provides everything, but protocols create boundaries. You get automatic wiring, compile-time safety, and value semantics.\n\n```swift\nstruct Provider: CheckoutServiceStack {\n    let database = PostgreSQL()\n    let stripe = StripeGateway()\n    \n    // Has ALL methods from ALL services\n    // But protocol constraints control access!\n}\n\nfunc processPayment\u003cS: PaymentService\u003e(service: S) {\n    // Can only see PaymentService methods\n    // Even though 'service' has everything\n}\n```\n\nThis is the magic: your provider is a \"superservice\" with all capabilities, but each use site only sees a narrow slice. The same struct, viewed through different protocol lenses, gives you free abstractions, explicit capabilities, and zero wiring.\n\n## Testing Without Mocks\n\nMock at your natural service boundaries, not where your architecture forces you. No mocking frameworks, no runtime injection, no fighting your design to write tests. Pick exactly the abstraction level that makes sense for what you're testing.\n\n### Unit Testing\n\nWhen testing a service in isolation, mock the layer directly below - not the entire tree:\n\n```swift\n@Test(\"Checkout coordinates order creation and payment\")\nfunc checkoutOrchestration() async throws {\n    struct MockOrderService: OrderService {\n        var lastCart: Cart?\n        func createOrder(from cart: Cart) async throws -\u003e Order {\n            lastCart = cart\n            return Order(id: \"test-123\", total: cart.total)\n        }\n        // We DON'T need to provide CartService, Database, etc.\n        // that OrderService normally requires!\n    }\n    \n    struct MockPaymentService: PaymentService {\n        var chargedAmount: Decimal?\n        var paymentToken: String?\n        func processPayment(for amount: Decimal, token: String) async throws {\n            chargedAmount = amount\n            paymentToken = token\n        }\n    }\n    \n    struct TestCheckout: CheckoutServiceAdapter {\n        let orderService = MockOrderService()\n        let paymentService = MockPaymentService() \n        let notificationService = MockNotificationService()\n    }\n    \n    let service = TestCheckout()\n    let order = try await service.checkout(cart: testCart, token: \"tok_test\")\n    \n    #expect(service.orderService.lastCart?.items == testCart.items)\n    #expect(service.paymentService.chargedAmount == order.total)\n    #expect(service.notificationService.sentEmails.count == 1)\n}\n```\n\nNotice how we only mock the services CheckoutService directly uses. We don't need to mock Database, Stripe, or any of the deeper dependencies. This keeps tests focused and fast.\n\n### Integration Testing\n\nSometimes you want confidence that your services work together correctly:\n\n```swift\n@Test(\"Order flows through the full service stack\")\nfunc checkoutIntegration() async throws {\n    struct TestProvider: CheckoutServiceAdapterStack {\n        let database = SQLite(\":memory:\")      // Real DB, test instance\n        let stripe = StripeTestMode()          // Real Stripe, test mode\n        let sendgrid = MockEmail()             // Capture emails\n    }\n    \n    let service = TestProvider()\n    \n    // The real service stack: CheckoutService → OrderService → CartService → Database\n    let order = try await service.checkout(cart: testCart, token: \"tok_test\")\n    \n    // Verify the order persisted through all layers\n    let saved = try await service.loadOrder(id: order.id)\n    #expect(saved.items.count == testCart.items.count)\n    \n    // Verify side effects\n    #expect(service.sendgrid.sentEmails.contains { $0.to == order.customerEmail })\n}\n```\n\nHere we use real implementations of our services with test versions of platform dependencies. This gives us confidence in our service integration without external side effects.\n\n### Scenario Testing\n\nSome scenarios require mixing real and mock services to properly test behavior:\n\n```swift\n@Test(\"Inventory reservation rollback on payment failure\")\nfunc inventoryRollback() async throws {\n    struct TestProvider: CheckoutServiceAdapterStack {\n        let database = PostgreSQL.testDB()        // REAL - need actual transactions\n        let stripe = FailAfterNSeconds(2)        // Fails after delay\n        let inventory = RealInventoryAPI()       // REAL - testing its rollback logic\n        let sendgrid = SpyEmailService()         // Spy - verify notifications\n    }\n    \n    let service = TestProvider()\n    let initialStock = try await service.inventory.getStock(itemId: \"widget\")\n    \n    await #expect(throws: PaymentError.timeout) {\n        try await service.checkout(cart: cartWithWidgets)\n    }\n    \n    // Real database + inventory means we can verify rollback actually worked\n    let finalStock = try await service.inventory.getStock(itemId: \"widget\")\n    #expect(finalStock == initialStock)  // Stock was restored\n    \n    // Spy lets us verify the right notifications went out\n    #expect(service.sendgrid.emails.contains { \n        $0.template == \"payment-failed\" \u0026\u0026 $0.data[\"reason\"] == \"timeout\"\n    })\n}\n```\n\nThe key insight: mock at the abstraction level that makes sense for what you're testing. Not always at the bottom, not always at the top - exactly where you need.\n\n## SwiftUI Integration\n\nThis pattern bridges the gap between SwiftUI's environment and your service layer, letting services access environment values while maintaining boundaries.\n\n### Environment-Based Service Providers\n\nSwiftUI's environment uses structural composition - just like our pattern. Service providers can pull any dependencies from the environment:\n\n```swift\nstruct CheckoutServiceProvider: CheckoutServiceAdapterStack, DynamicProperty {\n    @Environment(\\.database) var database\n    @Environment(\\.stripe) var stripe  \n    @Environment(\\.sendgrid) var sendgrid\n    \n    // Can use any property wrapper!\n    @State private var retryCount = 0\n    @Query(sort: \\.timestamp) private var recentOrders: [Order]\n    @AppStorage(\"user_id\") private var userId: String?\n}\n\nstruct CheckoutView: View {\n    let cart: Cart\n    \n    var body: some View {\n        CheckoutButton(\n            cart: cart,\n            service: CheckoutServiceProvider()\n        )\n    }\n}\n```\n\nThe structural typing works perfectly: SwiftUI provides values by key path, our providers consume them by property name. Same philosophy, seamless integration.\n\n### Previews with Mock Services\n\nCreate different service behaviors for SwiftUI previews:\n\n```swift\nstruct PreviewCheckoutProvider: CheckoutServiceAdapterStack {\n    let database = InMemoryDB()\n    let stripe: MockStripe\n    let sendgrid = MockEmail()\n}\n\n#Preview(\"Successful checkout\") {\n    CheckoutView(\n        cart: .sample,\n        service: PreviewCheckoutProvider(stripe: MockStripe(behavior: .success))\n    )\n}\n\n#Preview(\"Payment declined\") {\n    CheckoutView(\n        cart: .sample,\n        service: PreviewCheckoutProvider(stripe: MockStripe(behavior: .declined))\n    )\n}\n\n#Preview(\"Network error\") {\n    CheckoutView(\n        cart: .sample,\n        service: PreviewCheckoutProvider(stripe: MockStripe(behavior: .networkError))\n    )\n}\n```\n\nEach preview gets exactly the service behavior it needs to demonstrate that UI state.\n\n### Generic Views with Service Boundaries\n\nNot every view needs a service. Use service-generic views for components that orchestrate business logic:\n\n```swift\n// Service view - orchestrates business logic\nstruct CheckoutButton\u003cService: CheckoutService\u003e: View {\n    let cart: Cart\n    let service: Service\n    \n    @State private var isProcessing = false\n    @State private var error: Error?\n    \n    var body: some View {\n        Button(\"Checkout\") {\n            Task {\n                isProcessing = true\n                defer { isProcessing = false }\n                \n                do {\n                    let order = try await service.checkout(cart: cart)\n                    // Navigate to success\n                } catch {\n                    self.error = error\n                }\n            }\n        }\n        .disabled(isProcessing)\n        .alert(\"Checkout Failed\", isPresented: .constant(error != nil)) {\n            Button(\"OK\") { error = nil }\n        }\n    }\n}\n\n// Data view - just displays data\nstruct OrderRow: View {\n    let order: Order\n    \n    var body: some View {\n        HStack {\n            Text(order.id)\n            Spacer()\n            Text(order.total, format: .currency(code: \"USD\"))\n        }\n    }\n}\n\n// Container view - composes service and data views\nstruct CheckoutScreen: View {\n    let cart: Cart\n    \n    var body: some View {\n        VStack {\n            CartItemsList(items: cart.items)  // Data view\n            Divider()\n            CartTotalView(total: cart.total)  // Data view\n            CheckoutButton(                    // Service view\n                cart: cart,\n                service: CheckoutServiceProvider()\n            )\n        }\n    }\n}\n```\n\nOnly views that need to perform business operations should be generic over services. Most views just display data.\n\n## Installation\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/mattmassicotte/AdapterStack\", from: \"1.0.1\")\n]\n```\n\nThe macro saves one typealias per adapter. The pattern is just protocols.\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjadengeller%2Fadapterstack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjadengeller%2Fadapterstack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjadengeller%2Fadapterstack/lists"}