{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/functions/create/extension.ts"],"sourcesContent":["import type { Format, Partial } from \"ts-vista\";\n\nimport type { Extension, MergedExtensionDefine } from \"#/@types/extension\";\nimport type { StandardSchemaDictionary } from \"#/@types/standard\";\n\n/**\n * Recursively merge the `define` dictionaries of a tuple of extensions in\n * array order, then apply `ownDefine` last so the outer extension wins on key\n * collision.\n */\nconst flattenDefines = (\n    extensions: readonly Extension<StandardSchemaDictionary>[],\n    ownDefine: StandardSchemaDictionary,\n    visited: Set<Extension<StandardSchemaDictionary>> = new Set(),\n): StandardSchemaDictionary => {\n    const merged: Record<string, unknown> = {};\n\n    for (const extension of extensions) {\n        if (visited.has(extension)) continue;\n\n        visited.add(extension);\n\n        if (extension.extends) {\n            Object.assign(\n                merged,\n                flattenDefines(\n                    extension.extends,\n                    extension.define ?? {},\n                    visited,\n                ),\n            );\n        } else if (extension.define) {\n            Object.assign(merged, extension.define);\n        }\n    }\n\n    Object.assign(merged, ownDefine);\n\n    return merged as StandardSchemaDictionary;\n};\n\n/** The complete options for an extension. */\ntype CompleteExtensionOptions<\n    Define extends StandardSchemaDictionary,\n    Ext extends readonly Extension<StandardSchemaDictionary>[],\n> = {\n    /** Inner extensions to merge. */\n    extends: Ext;\n    /** The env variable definitions. */\n    define: Define;\n};\n\n/** Extension options with `extends`. */\ntype ExtensionOptions<\n    Define extends StandardSchemaDictionary,\n    Ext extends readonly Extension<StandardSchemaDictionary>[],\n> = Format<Partial<CompleteExtensionOptions<Define, Ext>, \"extends\">>;\n\n/**\n * Structural return type of `createExtension`.\n *\n * Identical in shape to `Extension<MergedExtensionDefine<Define, Ext>>` but\n * declared inline so that TypeScript does not re-check the\n * `StandardSchemaDictionary` constraint on the computed `MergedExtensionDefine`\n * at the generic declaration site.\n */\ntype FlattenedExtension<\n    Define extends StandardSchemaDictionary,\n    Ext extends\n        readonly Extension<StandardSchemaDictionary>[] = readonly never[],\n> = {\n    define: MergedExtensionDefine<Define, Ext>;\n};\n\n/**\n * Create an extension that can be passed to `createEnv` via the `extends`\n * option.\n *\n * Nested `define`s are flattened in array order.\n * The outer `define` wins on key collision.\n *\n * ### Example\n *\n * ```ts\n * const base = createExtension({\n *     define: {\n *         NODE_ENV: z.enum([\"development\", \"production\"]),\n *     },\n * });\n *\n * const withPort = createExtension({\n *     extends: [base],\n *     define: {\n *         PORT: z.number(),\n *     },\n * });\n *\n * const env = createEnv({\n *     target: \"server\",\n *     runtimeEnv: process.env,\n *     extends: [\n *          withPort,\n *     ],\n *     define: {\n *         API_URL: z.string().url(),\n *     },\n * });\n * ```\n */\nfunction createExtension<\n    const Define extends StandardSchemaDictionary,\n    const Ext extends\n        readonly Extension<StandardSchemaDictionary>[] = readonly never[],\n>(extension: ExtensionOptions<Define, Ext>): FlattenedExtension<Define, Ext>;\n\nfunction createExtension(\n    extension:\n        | ExtensionOptions<\n              StandardSchemaDictionary,\n              readonly Extension<StandardSchemaDictionary>[]\n          >\n        | ExtensionOptions<StandardSchemaDictionary, readonly never[]>,\n): FlattenedExtension<StandardSchemaDictionary> {\n    const ownDefine: StandardSchemaDictionary = extension.define ?? {};\n\n    const inner: readonly Extension<StandardSchemaDictionary>[] =\n        extension.extends ?? [];\n\n    const merged: StandardSchemaDictionary = flattenDefines(inner, ownDefine);\n\n    return {\n        define: merged,\n    } as FlattenedExtension<StandardSchemaDictionary>;\n}\n\nexport { createExtension };\n"],"mappings":";;;;;AAUA,MAAM,kBACF,YACA,WACA,0BAAoD,IAAI,IAAI,MACjC;CAC3B,MAAM,SAAkC,CAAC;CAEzC,KAAK,MAAM,aAAa,YAAY;EAChC,IAAI,QAAQ,IAAI,SAAS,GAAG;EAE5B,QAAQ,IAAI,SAAS;EAErB,IAAI,UAAU,SACV,OAAO,OACH,QACA,eACI,UAAU,SACV,UAAU,UAAU,CAAC,GACrB,OACJ,CACJ;OACG,IAAI,UAAU,QACjB,OAAO,OAAO,QAAQ,UAAU,MAAM;CAE9C;CAEA,OAAO,OAAO,QAAQ,SAAS;CAE/B,OAAO;AACX;AA4EA,SAAS,gBACL,WAM4C;CAC5C,MAAM,YAAsC,UAAU,UAAU,CAAC;CAOjE,OAAO,EACH,QAHqC,eAFrC,UAAU,WAAW,CAAC,GAEqC,SAG9C,EACjB;AACJ"}