@super-repo/envx 0.4.0 → 0.4.1-b.2
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.
- package/README.md +2 -2
- package/dist/auto.js +1 -1
- package/dist/chunks/auto-preload-CrSuZDg1.js +75 -0
- package/dist/chunks/auto-preload-CrSuZDg1.js.map +1 -0
- package/dist/chunks/aws-DgcXfw-Y.js +54 -0
- package/dist/chunks/aws-DgcXfw-Y.js.map +1 -0
- package/dist/chunks/azure-Cmh5-dPn.js +62 -0
- package/dist/chunks/azure-Cmh5-dPn.js.map +1 -0
- package/dist/chunks/{commands-KUyDszno.js → commands-Br0Z7uUF.js} +2 -2
- package/dist/chunks/commands-Br0Z7uUF.js.map +1 -0
- package/dist/chunks/doppler-BkQsajIp.js +50 -0
- package/dist/chunks/doppler-BkQsajIp.js.map +1 -0
- package/dist/chunks/gcp-Dq7QncPS.js +53 -0
- package/dist/chunks/gcp-Dq7QncPS.js.map +1 -0
- package/dist/chunks/infisical-CO073rdx.js +57 -0
- package/dist/chunks/infisical-CO073rdx.js.map +1 -0
- package/dist/chunks/{src-Ln2uXfYC.js → libs-CqVa6LY9.js} +0 -0
- package/dist/chunks/libs-CqVa6LY9.js.map +1 -0
- package/dist/chunks/op-CG9UWJIj.js +76 -0
- package/dist/chunks/op-CG9UWJIj.js.map +1 -0
- package/dist/chunks/runtime-BIEf_Dgo.js +102 -0
- package/dist/chunks/runtime-BIEf_Dgo.js.map +1 -0
- package/dist/chunks/{src-BM4EdT3z.js → src-ke3h417V.js} +2 -2
- package/dist/chunks/src-ke3h417V.js.map +1 -0
- package/dist/chunks/types-COrFYR0z.js +62 -0
- package/dist/chunks/types-COrFYR0z.js.map +1 -0
- package/dist/chunks/vault-BWdO9DFO.js +54 -0
- package/dist/chunks/vault-BWdO9DFO.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/commands/index.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/libs/audit.d.ts +62 -0
- package/dist/libs/audit.d.ts.map +1 -0
- package/dist/libs/config.d.ts +185 -0
- package/dist/libs/config.d.ts.map +1 -0
- package/dist/libs/crypto.d.ts +34 -0
- package/dist/libs/crypto.d.ts.map +1 -0
- package/dist/libs/decrypt.d.ts +10 -0
- package/dist/libs/decrypt.d.ts.map +1 -0
- package/dist/libs/encrypt.d.ts +21 -0
- package/dist/libs/encrypt.d.ts.map +1 -0
- package/dist/libs/env.d.ts +178 -0
- package/dist/libs/env.d.ts.map +1 -0
- package/dist/libs/expand.d.ts +51 -0
- package/dist/libs/expand.d.ts.map +1 -0
- package/dist/libs/index.d.ts +22 -0
- package/dist/libs/index.d.ts.map +1 -0
- package/dist/libs/index.js +2 -0
- package/dist/libs/keys.d.ts +92 -0
- package/dist/libs/keys.d.ts.map +1 -0
- package/dist/libs/match.d.ts +7 -0
- package/dist/libs/match.d.ts.map +1 -0
- package/dist/libs/parser.d.ts +33 -0
- package/dist/libs/parser.d.ts.map +1 -0
- package/dist/libs/rotate.d.ts +24 -0
- package/dist/libs/rotate.d.ts.map +1 -0
- package/dist/libs/types.d.ts +42 -0
- package/dist/libs/types.d.ts.map +1 -0
- package/dist/plugins/auto-preload.d.ts +50 -0
- package/dist/plugins/auto-preload.d.ts.map +1 -0
- package/dist/plugins/auto-preload.js +2 -0
- package/dist/plugins/aws.d.ts +52 -0
- package/dist/plugins/aws.d.ts.map +1 -0
- package/dist/plugins/aws.js +2 -0
- package/dist/plugins/azure.d.ts +46 -0
- package/dist/plugins/azure.d.ts.map +1 -0
- package/dist/plugins/azure.js +2 -0
- package/dist/plugins/doppler.d.ts +36 -0
- package/dist/plugins/doppler.d.ts.map +1 -0
- package/dist/plugins/doppler.js +2 -0
- package/dist/plugins/gcp.d.ts +48 -0
- package/dist/plugins/gcp.d.ts.map +1 -0
- package/dist/plugins/gcp.js +2 -0
- package/dist/plugins/index.d.ts +11 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +11 -0
- package/dist/plugins/infisical.d.ts +51 -0
- package/dist/plugins/infisical.d.ts.map +1 -0
- package/dist/plugins/infisical.js +2 -0
- package/dist/plugins/op.d.ts +52 -0
- package/dist/plugins/op.d.ts.map +1 -0
- package/dist/plugins/op.js +2 -0
- package/dist/plugins/runtime.d.ts +95 -0
- package/dist/plugins/runtime.d.ts.map +1 -0
- package/dist/plugins/runtime.js +2 -0
- package/dist/plugins/types.d.ts +54 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/vault.d.ts +47 -0
- package/dist/plugins/vault.d.ts.map +1 -0
- package/dist/plugins/vault.js +2 -0
- package/docs/plugins/custom-providers.md +26 -0
- package/docs/plugins/library-api.md +52 -0
- package/docs/plugins/overview.md +96 -0
- package/docs/plugins/providers.md +149 -0
- package/docs/plugins/recipes.md +77 -0
- package/docs/plugins/runtime.md +88 -0
- package/docs/security-models.md +3 -3
- package/package.json +51 -11
- package/dist/bin/dotenvx.d.ts +0 -1
- package/dist/bin/dotenvx.d.ts.map +0 -1
- package/dist/bin/dotenvx.js +0 -2
- package/dist/chunks/commands-KUyDszno.js.map +0 -1
- package/dist/chunks/src-BM4EdT3z.js.map +0 -1
- package/dist/chunks/src-Ln2uXfYC.js.map +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SecretProvider } from './types.js';
|
|
2
|
+
export interface AzureKeyVaultOptions {
|
|
3
|
+
/** Vault URL (e.g. "https://my-vault.vault.azure.net"). */
|
|
4
|
+
readonly vaultUrl: string;
|
|
5
|
+
/**
|
|
6
|
+
* Custom credential. Defaults to `new DefaultAzureCredential()` from
|
|
7
|
+
* `@azure/identity` — which walks env vars, managed identity, Azure
|
|
8
|
+
* CLI, etc. Override for tests or specialized credential chains.
|
|
9
|
+
*/
|
|
10
|
+
readonly credential?: unknown;
|
|
11
|
+
/** Pre-built SecretClient for tests. */
|
|
12
|
+
readonly client?: {
|
|
13
|
+
getSecret: (name: string, opts?: {
|
|
14
|
+
version?: string;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
value?: string | null;
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
19
|
+
/** Provider name, defaults to `"azure-keyvault"`. */
|
|
20
|
+
readonly name?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Azure Key Vault provider.
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { azureKeyVault } from "@super-repo/envx/plugins/azure";
|
|
27
|
+
*
|
|
28
|
+
* const az = azureKeyVault({ vaultUrl: "https://my-vault.vault.azure.net" });
|
|
29
|
+
* await az.preload(["prod-db", "prod-api"]);
|
|
30
|
+
*
|
|
31
|
+
* envx({ resolvers: { [az.name]: az.resolve } });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Reference shape:
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* DB_URL=${azure-keyvault:prod-db}
|
|
38
|
+
* API_KEY=${azure-keyvault:prod-api@<version>}
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Uses `@azure/keyvault-secrets` + `@azure/identity` (lazy-loaded):
|
|
42
|
+
*
|
|
43
|
+
* pnpm add @azure/keyvault-secrets @azure/identity
|
|
44
|
+
*/
|
|
45
|
+
export declare function azureKeyVault(opts: AzureKeyVaultOptions): SecretProvider;
|
|
46
|
+
//# sourceMappingURL=azure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"azure.d.ts","sourceRoot":"","sources":["../../src/plugins/azure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjF,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC;YAChE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SACvB,CAAC,CAAC;KACJ,CAAC;IACF,qDAAqD;IACrD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,cAAc,CAgDxE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SecretProvider } from './types.js';
|
|
2
|
+
export interface DopplerOptions {
|
|
3
|
+
/** Doppler service token (per-config). Required. */
|
|
4
|
+
readonly token: string;
|
|
5
|
+
/** Override Doppler's REST endpoint (testing / self-hosted). */
|
|
6
|
+
readonly endpoint?: string;
|
|
7
|
+
/** Override the global fetch (testing). */
|
|
8
|
+
readonly fetchImpl?: typeof fetch;
|
|
9
|
+
/** Provider name, defaults to `"doppler"`. */
|
|
10
|
+
readonly name?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Doppler provider — fetches secrets via the REST API using a service
|
|
14
|
+
* token. No SDK install required (uses native `fetch`).
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { doppler } from "@super-repo/envx/plugins/doppler";
|
|
18
|
+
*
|
|
19
|
+
* const dop = doppler({ token: process.env.DOPPLER_TOKEN! });
|
|
20
|
+
* await dop.preload(["DATABASE_URL", "API_KEY"]);
|
|
21
|
+
*
|
|
22
|
+
* envx({ resolvers: { [dop.name]: dop.resolve } });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Reference shape — IDs are Doppler secret names:
|
|
26
|
+
*
|
|
27
|
+
* ```
|
|
28
|
+
* DATABASE_URL=${doppler:DATABASE_URL}
|
|
29
|
+
* API_KEY=${doppler:API_KEY}
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Service-account tokens scope to one config (project + environment),
|
|
33
|
+
* so you typically don't need to specify project/config explicitly.
|
|
34
|
+
*/
|
|
35
|
+
export declare function doppler(opts: DopplerOptions): SecretProvider;
|
|
36
|
+
//# sourceMappingURL=doppler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doppler.d.ts","sourceRoot":"","sources":["../../src/plugins/doppler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAIhE,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,cAAc,CA0B5D"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SecretProvider } from './types.js';
|
|
2
|
+
export interface GcpSecretsOptions {
|
|
3
|
+
/** Google Cloud project ID (e.g. "acme-prod"). */
|
|
4
|
+
readonly projectId: string;
|
|
5
|
+
/**
|
|
6
|
+
* Default version alias. `"latest"` (the default) reads the most
|
|
7
|
+
* recent enabled version. Override per-id by including `@<version>`
|
|
8
|
+
* in the reference: `${gcp-secrets:my-secret@3}`.
|
|
9
|
+
*/
|
|
10
|
+
readonly version?: string;
|
|
11
|
+
/** Optional pre-built SecretManagerServiceClient (e.g. for tests). */
|
|
12
|
+
readonly client?: {
|
|
13
|
+
accessSecretVersion: (req: {
|
|
14
|
+
name: string;
|
|
15
|
+
}) => Promise<[{
|
|
16
|
+
payload?: {
|
|
17
|
+
data?: Buffer | string | null;
|
|
18
|
+
};
|
|
19
|
+
} | null, ...unknown[]]>;
|
|
20
|
+
};
|
|
21
|
+
/** Provider name, defaults to `"gcp-secrets"`. */
|
|
22
|
+
readonly name?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* GCP Secret Manager provider.
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { gcpSecrets } from "@super-repo/envx/plugins/gcp";
|
|
29
|
+
*
|
|
30
|
+
* const gcp = gcpSecrets({ projectId: "acme-prod" });
|
|
31
|
+
* await gcp.preload(["prod-db-url", "prod-api-key"]);
|
|
32
|
+
*
|
|
33
|
+
* envx({ resolvers: { [gcp.name]: gcp.resolve } });
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Reference shape:
|
|
37
|
+
*
|
|
38
|
+
* ```
|
|
39
|
+
* DB_URL=${gcp-secrets:prod-db-url} # uses default version (latest)
|
|
40
|
+
* API_KEY=${gcp-secrets:prod-api-key@3} # pin to version 3
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* Uses `@google-cloud/secret-manager` (lazy-loaded). Install:
|
|
44
|
+
*
|
|
45
|
+
* pnpm add @google-cloud/secret-manager
|
|
46
|
+
*/
|
|
47
|
+
export declare function gcpSecrets(opts: GcpSecretsOptions): SecretProvider;
|
|
48
|
+
//# sourceMappingURL=gcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp.d.ts","sourceRoot":"","sources":["../../src/plugins/gcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjF,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,mBAAmB,EAAE,CAAC,GAAG,EAAE;YACzB,IAAI,EAAE,MAAM,CAAC;SACd,KAAK,OAAO,CAAC,CAAC;YAAE,OAAO,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;aAAE,CAAA;SAAE,GAAG,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;KACvF,CAAC;IACF,kDAAkD;IAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,cAAc,CAgClE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { buildProvider, MissingSdkError, type SecretProvider, } from './types.js';
|
|
2
|
+
export { autoPreload, asResolvers, type AutoPreloadOptions } from './auto-preload.js';
|
|
3
|
+
export { createSecretRuntime, type SecretRuntime, type SecretRuntimeOptions, type SecretRuntimeStats, } from './runtime.js';
|
|
4
|
+
export { awsSecrets, type AwsSecretsOptions } from './aws.js';
|
|
5
|
+
export { gcpSecrets, type GcpSecretsOptions } from './gcp.js';
|
|
6
|
+
export { azureKeyVault, type AzureKeyVaultOptions } from './azure.js';
|
|
7
|
+
export { hcVault, type HcVaultOptions } from './vault.js';
|
|
8
|
+
export { onePassword, type OnePasswordOptions } from './op.js';
|
|
9
|
+
export { doppler, type DopplerOptions } from './doppler.js';
|
|
10
|
+
export { infisical, type InfisicalOptions } from './infisical.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,aAAa,EACb,eAAe,EACf,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAMtF,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { n as buildProvider, t as MissingSdkError } from "../chunks/types-COrFYR0z.js";
|
|
2
|
+
import { n as autoPreload, t as asResolvers } from "../chunks/auto-preload-CrSuZDg1.js";
|
|
3
|
+
import { t as createSecretRuntime } from "../chunks/runtime-BIEf_Dgo.js";
|
|
4
|
+
import { t as awsSecrets } from "../chunks/aws-DgcXfw-Y.js";
|
|
5
|
+
import { t as gcpSecrets } from "../chunks/gcp-Dq7QncPS.js";
|
|
6
|
+
import { t as azureKeyVault } from "../chunks/azure-Cmh5-dPn.js";
|
|
7
|
+
import { t as hcVault } from "../chunks/vault-BWdO9DFO.js";
|
|
8
|
+
import { t as onePassword } from "../chunks/op-CG9UWJIj.js";
|
|
9
|
+
import { t as doppler } from "../chunks/doppler-BkQsajIp.js";
|
|
10
|
+
import { t as infisical } from "../chunks/infisical-CO073rdx.js";
|
|
11
|
+
export { MissingSdkError, asResolvers, autoPreload, awsSecrets, azureKeyVault, buildProvider, createSecretRuntime, doppler, gcpSecrets, hcVault, infisical, onePassword };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SecretProvider } from './types.js';
|
|
2
|
+
export interface InfisicalOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Service token or universal-auth access token. Required. Pass a
|
|
5
|
+
* universal-auth token from your machine identity flow, or a project
|
|
6
|
+
* service token for simpler setups.
|
|
7
|
+
*/
|
|
8
|
+
readonly token: string;
|
|
9
|
+
/** Project ID (workspace ID, "workspaceId"). */
|
|
10
|
+
readonly projectId: string;
|
|
11
|
+
/** Environment slug — `dev`, `staging`, `prod`, etc. */
|
|
12
|
+
readonly environment: string;
|
|
13
|
+
/**
|
|
14
|
+
* Folder path inside the environment. Defaults to `/`.
|
|
15
|
+
*/
|
|
16
|
+
readonly secretPath?: string;
|
|
17
|
+
/** Self-hosted Infisical endpoint. Defaults to the SaaS host. */
|
|
18
|
+
readonly endpoint?: string;
|
|
19
|
+
/** Override the global fetch (testing). */
|
|
20
|
+
readonly fetchImpl?: typeof fetch;
|
|
21
|
+
/** Provider name, defaults to `"infisical"`. */
|
|
22
|
+
readonly name?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Infisical provider — uses the v3 REST API with a service / machine
|
|
26
|
+
* identity token. No SDK install required (uses native `fetch`).
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { infisical } from "@super-repo/envx/plugins/infisical";
|
|
30
|
+
*
|
|
31
|
+
* const inf = infisical({
|
|
32
|
+
* token: process.env.INFISICAL_TOKEN!,
|
|
33
|
+
* projectId: process.env.INFISICAL_PROJECT_ID!,
|
|
34
|
+
* environment: "prod",
|
|
35
|
+
* });
|
|
36
|
+
* await inf.preload(["DATABASE_URL", "API_KEY"]);
|
|
37
|
+
*
|
|
38
|
+
* envx({ resolvers: { [inf.name]: inf.resolve } });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Reference shape — IDs are Infisical secret names:
|
|
42
|
+
*
|
|
43
|
+
* ```
|
|
44
|
+
* DATABASE_URL=${infisical:DATABASE_URL}
|
|
45
|
+
* API_KEY=${infisical:API_KEY}
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* Self-hosted? Pass `endpoint: "https://infisical.example.com"`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function infisical(opts: InfisicalOptions): SecretProvider;
|
|
51
|
+
//# sourceMappingURL=infisical.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infisical.d.ts","sourceRoot":"","sources":["../../src/plugins/infisical.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAIhE,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,gDAAgD;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,cAAc,CAkChE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { SecretProvider } from './types.js';
|
|
2
|
+
export interface OnePasswordOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Service account token. When set, the plugin uses the @1password/sdk
|
|
5
|
+
* directly. When unset, it falls back to spawning the local `op` CLI
|
|
6
|
+
* (which gets credentials from your signed-in session).
|
|
7
|
+
*/
|
|
8
|
+
readonly token?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Override the SDK / CLI loader. Useful for tests — pass a function
|
|
11
|
+
* that returns the secret value for a given reference.
|
|
12
|
+
*/
|
|
13
|
+
readonly fetcher?: (ref: string) => Promise<string>;
|
|
14
|
+
/** Provider name, defaults to `"op"` (matches the conventional `op://` URI scheme). */
|
|
15
|
+
readonly name?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 1Password provider.
|
|
19
|
+
*
|
|
20
|
+
* Two execution modes:
|
|
21
|
+
* - **SDK mode** (when `token` is set): uses `@1password/sdk`, no CLI needed.
|
|
22
|
+
* - **CLI mode** (when `token` is unset): exec's the `op` binary,
|
|
23
|
+
* reads creds from your signed-in session. No SDK install required.
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { onePassword } from "@super-repo/envx/plugins/op";
|
|
27
|
+
*
|
|
28
|
+
* // CLI mode — works on developer machines that ran `op signin` once.
|
|
29
|
+
* const op = onePassword();
|
|
30
|
+
*
|
|
31
|
+
* // SDK mode — for CI / containers.
|
|
32
|
+
* const op = onePassword({ token: process.env.OP_SERVICE_ACCOUNT_TOKEN! });
|
|
33
|
+
*
|
|
34
|
+
* await op.preload([
|
|
35
|
+
* "op://prod/Database/url",
|
|
36
|
+
* "op://prod/API/key",
|
|
37
|
+
* ]);
|
|
38
|
+
*
|
|
39
|
+
* envx({ resolvers: { [op.name]: op.resolve } });
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* Reference shape — pass the full `op://vault/item/field` URI:
|
|
43
|
+
*
|
|
44
|
+
* ```
|
|
45
|
+
* DB_URL=${op:op://prod/Database/url}
|
|
46
|
+
* API_KEY=${op:op://prod/API/key}
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* SDK install (only if you set `token`): `pnpm add @1password/sdk`
|
|
50
|
+
*/
|
|
51
|
+
export declare function onePassword(opts?: OnePasswordOptions): SecretProvider;
|
|
52
|
+
//# sourceMappingURL=op.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"op.d.ts","sourceRoot":"","sources":["../../src/plugins/op.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjF,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,uFAAuF;IACvF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,cAAc,CAuDzE"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { SecretProvider } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Async-first secret accessor for runtime / edge code.
|
|
4
|
+
*
|
|
5
|
+
* Use this when the security model is "secrets never leave the source"
|
|
6
|
+
* — the app references secrets like `${vault:prod/db}` (or by structured
|
|
7
|
+
* key), and the values are fetched on first read, cached for `ttl`
|
|
8
|
+
* seconds, then refreshed. Nothing plaintext touches disk; nothing is
|
|
9
|
+
* baked into the build artifact.
|
|
10
|
+
*
|
|
11
|
+
* Designed to run in any V8 isolate — Cloudflare Workers, Vercel Edge,
|
|
12
|
+
* Deno Deploy, AWS Lambda, Bun, Node. No `fs`, no `child_process`, no
|
|
13
|
+
* dynamic require. Plugin compatibility is the only constraint:
|
|
14
|
+
* fetch-only providers (Vault, Doppler, Infisical, custom HTTP) work
|
|
15
|
+
* everywhere; SDK-backed providers (AWS, GCP, Azure, 1Password SDK)
|
|
16
|
+
* work in Node-compatible runtimes only.
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* // src/secrets.ts — module-level singleton, one per worker
|
|
20
|
+
* import { createSecretRuntime } from "@super-repo/envx/plugins/runtime";
|
|
21
|
+
* import { hcVault } from "@super-repo/envx/plugins/vault";
|
|
22
|
+
*
|
|
23
|
+
* export const secrets = createSecretRuntime({
|
|
24
|
+
* providers: [
|
|
25
|
+
* hcVault({ endpoint: env.VAULT_ADDR, token: env.VAULT_TOKEN }),
|
|
26
|
+
* ],
|
|
27
|
+
* ttl: 300, // refresh every 5 minutes
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // src/handler.ts
|
|
31
|
+
* export default async function handler(req: Request): Promise<Response> {
|
|
32
|
+
* const dbUrl = await secrets.get("vault:prod/db");
|
|
33
|
+
* return new Response(...);
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* Cold starts: each worker instance fetches lazily on first reference.
|
|
38
|
+
* Subsequent reads within the TTL window hit the in-memory cache.
|
|
39
|
+
*/
|
|
40
|
+
export interface SecretRuntimeOptions {
|
|
41
|
+
/** Providers to dispatch references to. Identified by their `name`. */
|
|
42
|
+
readonly providers: readonly SecretProvider[];
|
|
43
|
+
/**
|
|
44
|
+
* Cache TTL in seconds. After this window, the next `get()` triggers a
|
|
45
|
+
* fresh fetch. `0` = no caching (fetch every read — only sensible
|
|
46
|
+
* for very-low-traffic edges). `Infinity` = cache forever.
|
|
47
|
+
*
|
|
48
|
+
* Default: 300 (5 minutes).
|
|
49
|
+
*/
|
|
50
|
+
readonly ttl?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Cache mode for fetch failures.
|
|
53
|
+
*
|
|
54
|
+
* - `"throw"` (default): rethrow the error from `get()`. Caller decides.
|
|
55
|
+
* - `"cache-stale"`: when a refresh fails, return the previously cached
|
|
56
|
+
* value (if any) and log a warning. Useful when intermittent
|
|
57
|
+
* network blips shouldn't take the whole request down.
|
|
58
|
+
* - `"cache-error"`: cache the error for `errorTtl` seconds so we don't
|
|
59
|
+
* hammer a failing backend. The error is rethrown until expiry.
|
|
60
|
+
*/
|
|
61
|
+
readonly onFailure?: "throw" | "cache-stale" | "cache-error";
|
|
62
|
+
/** Seconds to cache a failed lookup when `onFailure: "cache-error"`. Default 30. */
|
|
63
|
+
readonly errorTtl?: number;
|
|
64
|
+
/** Optional `console`-like sink for warnings. Defaults to `console`. */
|
|
65
|
+
readonly logger?: {
|
|
66
|
+
warn(msg: string): void;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export interface SecretRuntime {
|
|
70
|
+
/**
|
|
71
|
+
* Resolve `${provider:id}` (or `provider:id` without braces) to the
|
|
72
|
+
* current value. Async — triggers a network fetch on cache miss /
|
|
73
|
+
* stale read. Throws on missing provider. Behavior on fetch failure
|
|
74
|
+
* is governed by `onFailure`.
|
|
75
|
+
*/
|
|
76
|
+
get(ref: string): Promise<string>;
|
|
77
|
+
/** Same as `get`, but returns `undefined` instead of throwing on miss. */
|
|
78
|
+
tryGet(ref: string): Promise<string | undefined>;
|
|
79
|
+
/**
|
|
80
|
+
* Force-refresh a specific ref now, ignoring the TTL. Useful for
|
|
81
|
+
* post-rotation cache busts or webhook-driven invalidation.
|
|
82
|
+
*/
|
|
83
|
+
invalidate(ref: string): Promise<string>;
|
|
84
|
+
/** Drop everything. Next reads will fetch fresh. */
|
|
85
|
+
clear(): void;
|
|
86
|
+
/** Shape inspection — useful in tests / dashboards. */
|
|
87
|
+
readonly stats: () => SecretRuntimeStats;
|
|
88
|
+
}
|
|
89
|
+
export interface SecretRuntimeStats {
|
|
90
|
+
readonly cached: number;
|
|
91
|
+
readonly inflight: number;
|
|
92
|
+
readonly errors: number;
|
|
93
|
+
}
|
|
94
|
+
export declare function createSecretRuntime(opts: SecretRuntimeOptions): SecretRuntime;
|
|
95
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/plugins/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,WAAW,oBAAoB;IACnC,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;IAC9C;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;OASG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,aAAa,GAAG,aAAa,CAAC;IAC7D,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAElC,0EAA0E;IAC1E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAEjD;;;OAGG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEzC,oDAAoD;IACpD,KAAK,IAAI,IAAI,CAAC;IAEd,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,kBAAkB,CAAC;CAC1C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAoBD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,aAAa,CAgI7E"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape every plugin returns. envx's `resolvers:` config is sync
|
|
3
|
+
* (we don't want loadEnv to become async-everywhere), but every real
|
|
4
|
+
* secret-store SDK is async — so plugins split the concern in two:
|
|
5
|
+
*
|
|
6
|
+
* 1. `preload(ids)` — async batch fetch, populates an in-memory cache.
|
|
7
|
+
* 2. `resolve(id)` — sync cache lookup. This is what envx wires into `resolvers`.
|
|
8
|
+
*
|
|
9
|
+
* Call `preload(...)` once during your bootstrap (top-level `await` in an
|
|
10
|
+
* async entry, or a small `await main()` wrapper), then hand `resolve` to
|
|
11
|
+
* envx. The cache is per-provider-instance — instantiate once and share.
|
|
12
|
+
*/
|
|
13
|
+
export interface SecretProvider {
|
|
14
|
+
/** Stable provider name. Matches the prefix in `${name:id}` env-file refs. */
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/**
|
|
17
|
+
* Fetch and cache the named secrets. Safe to call multiple times — the
|
|
18
|
+
* implementation should de-duplicate and skip already-cached IDs.
|
|
19
|
+
* Errors fetching individual secrets are surfaced as exceptions so
|
|
20
|
+
* the bootstrap fails fast (rather than silently leaving secrets unset).
|
|
21
|
+
*/
|
|
22
|
+
preload(ids: readonly string[]): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Fetch a single secret directly from the backend, bypassing the
|
|
25
|
+
* plugin's cache. Used by the runtime/edge accessor (which owns its
|
|
26
|
+
* own TTL cache and can't accept a "stuck-forever" plugin cache).
|
|
27
|
+
*/
|
|
28
|
+
fetch(id: string): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Read a previously preloaded value. Returns `undefined` for unknown
|
|
31
|
+
* IDs so envx leaves the literal `${name:id}` in place — that way
|
|
32
|
+
* misconfigurations surface visibly instead of producing an empty
|
|
33
|
+
* string at runtime.
|
|
34
|
+
*/
|
|
35
|
+
resolve(id: string): string | undefined;
|
|
36
|
+
/** Direct access to the cache for tooling (testing, debugging, snapshotting). */
|
|
37
|
+
readonly cache: ReadonlyMap<string, string>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Internal factory used by every plugin to wire up the preload / resolve
|
|
41
|
+
* / cache trio. Plugin authors hand it a `fetchOne(id)` and we handle
|
|
42
|
+
* the cache + parallelism. Keeps the per-provider files focused on the
|
|
43
|
+
* SDK call.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildProvider(name: string, fetchOne: (id: string) => Promise<string>): SecretProvider;
|
|
46
|
+
/**
|
|
47
|
+
* Thrown by every plugin's lazy SDK loader when the consumer hasn't
|
|
48
|
+
* installed the relevant SDK package. The message includes the
|
|
49
|
+
* `pnpm add <pkg>` line so users get the exact remediation.
|
|
50
|
+
*/
|
|
51
|
+
export declare class MissingSdkError extends Error {
|
|
52
|
+
constructor(plugin: string, sdk: string);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/plugins/types.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;;OAKG;IACH,OAAO,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;;;OAIG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAExC,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7C;AAMD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GACxC,cAAc,CAwChB;AAMD;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;CAQxC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SecretProvider } from './types.js';
|
|
2
|
+
export interface HcVaultOptions {
|
|
3
|
+
/** Vault server URL, e.g. "https://vault.example.com:8200". */
|
|
4
|
+
readonly endpoint: string;
|
|
5
|
+
/** Vault token (from `VAULT_TOKEN`, machine identity, etc.). */
|
|
6
|
+
readonly token: string;
|
|
7
|
+
/**
|
|
8
|
+
* KV v2 mount path (default: `"secret"`). KV v1 callers should set
|
|
9
|
+
* `kvVersion: 1`.
|
|
10
|
+
*/
|
|
11
|
+
readonly mount?: string;
|
|
12
|
+
/** KV engine version. Default `2`. */
|
|
13
|
+
readonly kvVersion?: 1 | 2;
|
|
14
|
+
/** Override the global fetch (testing). */
|
|
15
|
+
readonly fetchImpl?: typeof fetch;
|
|
16
|
+
/** Provider name, defaults to `"vault"`. */
|
|
17
|
+
readonly name?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* HashiCorp Vault (KV v1 / v2) provider.
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { hcVault } from "@super-repo/envx/plugins/vault";
|
|
24
|
+
*
|
|
25
|
+
* const vault = hcVault({
|
|
26
|
+
* endpoint: process.env.VAULT_ADDR!,
|
|
27
|
+
* token: process.env.VAULT_TOKEN!,
|
|
28
|
+
* mount: "secret",
|
|
29
|
+
* });
|
|
30
|
+
* await vault.preload(["prod/db", "prod/api"]);
|
|
31
|
+
*
|
|
32
|
+
* envx({ resolvers: { [vault.name]: vault.resolve } });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* Reference shape:
|
|
36
|
+
*
|
|
37
|
+
* ```
|
|
38
|
+
* # KV v2 path: <mount>/data/<id>, returns the "value" field by default.
|
|
39
|
+
* # Or pin a specific JSON field: ${vault:prod/db#username}
|
|
40
|
+
* DB_URL=${vault:prod/db}
|
|
41
|
+
* USER=${vault:prod/db#username}
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* No SDK install required — uses native `fetch` (Node 20+).
|
|
45
|
+
*/
|
|
46
|
+
export declare function hcVault(opts: HcVaultOptions): SecretProvider;
|
|
47
|
+
//# sourceMappingURL=vault.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../src/plugins/vault.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAIhE,MAAM,WAAW,cAAc;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,sCAAsC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,cAAc,CAuC5D"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Custom providers
|
|
2
|
+
|
|
3
|
+
The same `SecretProvider` shape works for any backend — wrap a fetcher with `buildProvider` and envx treats it like a first-party plugin.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { buildProvider, type SecretProvider } from "@super-repo/envx/plugins";
|
|
7
|
+
|
|
8
|
+
const myProvider: SecretProvider = buildProvider("my-backend", async (id) => {
|
|
9
|
+
const r = await fetch(`https://internal.example.com/secrets/${id}`, {
|
|
10
|
+
headers: { Authorization: `Bearer ${process.env.MY_TOKEN}` },
|
|
11
|
+
});
|
|
12
|
+
if (!r.ok) throw new Error(`fetch ${id}: ${r.status}`);
|
|
13
|
+
const body = await r.json() as { value: string };
|
|
14
|
+
return body.value;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
await myProvider.preload(["a", "b"]);
|
|
18
|
+
envx({ resolvers: { [myProvider.name]: myProvider.resolve } });
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
SOMETHING=${my-backend:a}
|
|
23
|
+
ELSE=${my-backend:b}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Because `buildProvider` only depends on what your `fetchOne` does, custom providers backed by `fetch` are the most portable option for edge runtimes — see the compatibility table in [runtime.md](./runtime.md#edge-runtime-compatibility-per-provider).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Library API
|
|
2
|
+
|
|
3
|
+
The shared shape every plugin returns, plus the helpers that wire them into envx.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
interface SecretProvider {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
preload(ids: readonly string[]): Promise<void>; // batch fetch + cache
|
|
9
|
+
fetch(id: string): Promise<string>; // single fetch, bypasses cache (for runtime use)
|
|
10
|
+
resolve(id: string): string | undefined; // sync cache read (for envx resolvers)
|
|
11
|
+
readonly cache: ReadonlyMap<string, string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function autoPreload(
|
|
15
|
+
providers: readonly SecretProvider[],
|
|
16
|
+
opts: { envFiles: string[]; cwd?: string },
|
|
17
|
+
): Promise<{ preloaded: Record<string, string[]> }>;
|
|
18
|
+
|
|
19
|
+
function asResolvers(
|
|
20
|
+
providers: readonly SecretProvider[],
|
|
21
|
+
): Record<string, (id: string) => string | undefined>;
|
|
22
|
+
|
|
23
|
+
function buildProvider(
|
|
24
|
+
name: string,
|
|
25
|
+
fetchOne: (id: string) => Promise<string>,
|
|
26
|
+
): SecretProvider;
|
|
27
|
+
|
|
28
|
+
class MissingSdkError extends Error { /* … */ }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## How it composes with envx
|
|
32
|
+
|
|
33
|
+
envx's `resolvers:` config takes a **synchronous** function (`(id) => string | undefined`). All real secret-store SDKs are async, so plugins split the concern in two:
|
|
34
|
+
|
|
35
|
+
1. **`preload(ids)`** — async batch fetch, populates an in-memory cache.
|
|
36
|
+
2. **`resolve(id)`** — sync cache read. This is what envx calls during load.
|
|
37
|
+
|
|
38
|
+
You call `preload` in your bootstrap (top-level `await` in an async entry, or inside a small `await main()` wrapper), then hand `resolve` to envx. The cache is per-provider-instance — one `awsSecrets({...})` call yields one shared cache.
|
|
39
|
+
|
|
40
|
+
## Subpath imports vs. barrel
|
|
41
|
+
|
|
42
|
+
Per-provider subpaths skip the other providers' dynamic-import code paths entirely, so they're preferred when bundle size matters:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// Preferred — only the AWS plugin's lazy-import lives in the bundle
|
|
46
|
+
import { awsSecrets } from "@super-repo/envx/plugins/aws";
|
|
47
|
+
|
|
48
|
+
// Convenience — barrel re-exports everything for one-line imports
|
|
49
|
+
import { awsSecrets, gcpSecrets, asResolvers } from "@super-repo/envx/plugins";
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The runtime entry has its own subpath: `@super-repo/envx/plugins/runtime` — see [runtime.md](./runtime.md).
|