Handling extensions in a configurable API like `@effect/sql-pglite` can be a bit tricky, especial...

I'm building an @effect/sql-pglite package so I can use https://pglite.dev/ with effect-sql. I've got it all set up and working but am having trouble figuring out the best pattern to deal with extensions. PGlite can load extensions and their apis get namespaced under the client. Example:

const db = await PGlite.create({
    dataDir: "memory://",
    extensions: {
        electric: electricSync(),
        live,
        vector
    }
})
// now can access db.live and db.electric


What's the best way to represent this with Layer and Tag? I've come up with this and it mostly works:

export const layerWithExtensionsTagged = <T extends Record<string, any>, Tag extends string>(
    tag: Tag,
    config: Omit<PgLiteClientConfig<ExtensionsToNamespaces<T>>, "extensions">,
    extensions: T
): {
    layer: Layer.Layer<
        PgLiteClient<ExtensionsToNamespaces<T>> | Client.SqlClient,
        ConfigError | SqlError
    >
    tag: Context.Tag<Tag, PgLiteClient<ExtensionsToNamespaces<T>>>
} => {
    return {
        tag: Context.Tag(tag)<Tag, PgLiteClient<ExtensionsToNamespaces<T>>>(),
        layer: layer<ExtensionsToNamespaces<T>>({
            ...config,
            extensions: {
                ...extensions
            }
        } as PgLiteClientConfig<ExtensionsToNamespaces<T>>)
    }
}


The only problem is when using the tag returned by this function the R of the effect contains "MyStringTag" instead of a type like it usually does with Tag/Service definitions. I don't know if that presents a problem in practice.

What's the best way to deal with this sort of configurable API for services?
Was this page helpful?