@super-repo/envx-plugins 0.2.3-b.4

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.
Files changed (47) hide show
  1. package/README.md +380 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/auto-preload.d.ts +50 -0
  4. package/dist/auto-preload.d.ts.map +1 -0
  5. package/dist/auto-preload.js +81 -0
  6. package/dist/auto-preload.js.map +1 -0
  7. package/dist/aws.d.ts +52 -0
  8. package/dist/aws.d.ts.map +1 -0
  9. package/dist/aws.js +68 -0
  10. package/dist/aws.js.map +1 -0
  11. package/dist/azure.d.ts +46 -0
  12. package/dist/azure.d.ts.map +1 -0
  13. package/dist/azure.js +64 -0
  14. package/dist/azure.js.map +1 -0
  15. package/dist/doppler.d.ts +36 -0
  16. package/dist/doppler.d.ts.map +1 -0
  17. package/dist/doppler.js +48 -0
  18. package/dist/doppler.js.map +1 -0
  19. package/dist/gcp.d.ts +48 -0
  20. package/dist/gcp.d.ts.map +1 -0
  21. package/dist/gcp.js +56 -0
  22. package/dist/gcp.js.map +1 -0
  23. package/dist/index.d.ts +11 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +18 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/infisical.d.ts +51 -0
  28. package/dist/infisical.d.ts.map +1 -0
  29. package/dist/infisical.js +59 -0
  30. package/dist/infisical.js.map +1 -0
  31. package/dist/op.d.ts +52 -0
  32. package/dist/op.d.ts.map +1 -0
  33. package/dist/op.js +85 -0
  34. package/dist/op.js.map +1 -0
  35. package/dist/runtime.d.ts +95 -0
  36. package/dist/runtime.d.ts.map +1 -0
  37. package/dist/runtime.js +124 -0
  38. package/dist/runtime.js.map +1 -0
  39. package/dist/types.d.ts +54 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +64 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/vault.d.ts +47 -0
  44. package/dist/vault.d.ts.map +1 -0
  45. package/dist/vault.js +63 -0
  46. package/dist/vault.js.map +1 -0
  47. package/package.json +79 -0
package/dist/op.d.ts ADDED
@@ -0,0 +1,52 @@
1
+ import { type 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/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"}
package/dist/op.js ADDED
@@ -0,0 +1,85 @@
1
+ import { execFile } from "child_process";
2
+ import { promisify } from "util";
3
+ import { buildProvider, MissingSdkError } from "./types.js";
4
+ const execFileAsync = promisify(execFile);
5
+ /**
6
+ * 1Password provider.
7
+ *
8
+ * Two execution modes:
9
+ * - **SDK mode** (when `token` is set): uses `@1password/sdk`, no CLI needed.
10
+ * - **CLI mode** (when `token` is unset): exec's the `op` binary,
11
+ * reads creds from your signed-in session. No SDK install required.
12
+ *
13
+ * ```ts
14
+ * import { onePassword } from "@super-repo/envx-plugins/op";
15
+ *
16
+ * // CLI mode — works on developer machines that ran `op signin` once.
17
+ * const op = onePassword();
18
+ *
19
+ * // SDK mode — for CI / containers.
20
+ * const op = onePassword({ token: process.env.OP_SERVICE_ACCOUNT_TOKEN! });
21
+ *
22
+ * await op.preload([
23
+ * "op://prod/Database/url",
24
+ * "op://prod/API/key",
25
+ * ]);
26
+ *
27
+ * envx({ resolvers: { [op.name]: op.resolve } });
28
+ * ```
29
+ *
30
+ * Reference shape — pass the full `op://vault/item/field` URI:
31
+ *
32
+ * ```
33
+ * DB_URL=${op:op://prod/Database/url}
34
+ * API_KEY=${op:op://prod/API/key}
35
+ * ```
36
+ *
37
+ * SDK install (only if you set `token`): `pnpm add @1password/sdk`
38
+ */
39
+ export function onePassword(opts = {}) {
40
+ const name = opts.name ?? "op";
41
+ let fetcher = opts.fetcher;
42
+ return buildProvider(name, async (ref) => {
43
+ if (!fetcher) {
44
+ if (opts.token) {
45
+ let sdk;
46
+ try {
47
+ const specifier = "@1password/sdk";
48
+ sdk = (await import(/* @vite-ignore */ specifier));
49
+ }
50
+ catch {
51
+ throw new MissingSdkError("1password", "@1password/sdk");
52
+ }
53
+ const client = await sdk.createClient({
54
+ auth: opts.token,
55
+ integrationName: "envx-plugins",
56
+ integrationVersion: "0.1.0",
57
+ });
58
+ fetcher = (r) => client.secrets.resolve(r);
59
+ }
60
+ else {
61
+ // CLI mode — `op read <ref>`
62
+ fetcher = async (r) => {
63
+ try {
64
+ const { stdout } = await execFileAsync("op", ["read", r], {
65
+ encoding: "utf8",
66
+ });
67
+ return stdout.trim();
68
+ }
69
+ catch (e) {
70
+ const err = e;
71
+ if (err.code === "ENOENT") {
72
+ throw new Error("[ENVX_PLUGIN_OP_CLI] the `op` CLI is not on PATH.\n" +
73
+ " install: https://developer.1password.com/docs/cli\n" +
74
+ " or run with a service-account token: onePassword({ token: ... })");
75
+ }
76
+ throw new Error(`op read '${r}' failed: ${err.stderr?.trim() || err.message}`);
77
+ }
78
+ };
79
+ }
80
+ }
81
+ return fetcher(ref);
82
+ });
83
+ }
84
+ // #endregion -----------------------------------------------
85
+ //# sourceMappingURL=op.js.map
package/dist/op.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"op.js","sourceRoot":"","sources":["../src/op.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAuB,MAAM,YAAY,CAAC;AAEjF,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAoB1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B,EAAE;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;IAE/B,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAE3B,OAAO,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBASf,IAAI,GAAU,CAAC;gBACf,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,gBAAgB,CAAC;oBACnC,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAqB,CAAC;gBACzE,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,IAAI,eAAe,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;gBAC3D,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC;oBACpC,IAAI,EAAE,IAAI,CAAC,KAAK;oBAChB,eAAe,EAAE,cAAc;oBAC/B,kBAAkB,EAAE,OAAO;iBAC5B,CAAC,CAAC;gBACH,OAAO,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACrD,CAAC;iBAAM,CAAC;gBACN,6BAA6B;gBAC7B,OAAO,GAAG,KAAK,EAAE,CAAS,EAAmB,EAAE;oBAC7C,IAAI,CAAC;wBACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE;4BACxD,QAAQ,EAAE,MAAM;yBACjB,CAAC,CAAC;wBACH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;oBACvB,CAAC;oBAAC,OAAO,CAAC,EAAE,CAAC;wBACX,MAAM,GAAG,GAAG,CAAgD,CAAC;wBAC7D,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;4BAC1B,MAAM,IAAI,KAAK,CACb,qDAAqD;gCACnD,uDAAuD;gCACvD,oEAAoE,CACvE,CAAC;wBACJ,CAAC;wBACD,MAAM,IAAI,KAAK,CACb,YAAY,CAAC,aAAa,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,EAAE,CAC9D,CAAC;oBACJ,CAAC;gBACH,CAAC,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,6DAA6D"}
@@ -0,0 +1,95 @@
1
+ import type { 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/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,124 @@
1
+ // Accept all four shapes for ergonomics — the runtime is JS-only so we
2
+ // don't have dotenvx to worry about, but consistency with env-file syntax
3
+ // matters for muscle memory:
4
+ // - `provider:id` — bare
5
+ // - `${provider:id}` — single-brace (rejected by dotenvx in env files, fine here)
6
+ // - `${{provider:id}}` — canonical (matches env-file syntax)
7
+ const REF_RE = /^\$?\{?\{?([a-zA-Z][\w-]*):([^}]+?)\}?\}?$/;
8
+ export function createSecretRuntime(opts) {
9
+ const ttl = opts.ttl ?? 300;
10
+ const onFailure = opts.onFailure ?? "throw";
11
+ const errorTtl = opts.errorTtl ?? 30;
12
+ const logger = opts.logger ?? {
13
+ warn: (m) => {
14
+ // eslint-disable-next-line no-console
15
+ console.warn(m);
16
+ },
17
+ };
18
+ const byName = new Map();
19
+ for (const p of opts.providers)
20
+ byName.set(p.name, p);
21
+ const cache = new Map();
22
+ const errors = new Map();
23
+ // Coalesce concurrent reads of the same ref so we never fan out N
24
+ // fetches for one secret across N requests. Standard single-flight
25
+ // pattern, scoped to this runtime instance.
26
+ const inflight = new Map();
27
+ const now = () => Date.now();
28
+ function parseRef(ref) {
29
+ const m = REF_RE.exec(ref);
30
+ if (!m) {
31
+ throw new Error(`[ENVX_RUNTIME_BAD_REF] '${ref}' is not a 'provider:id' reference`);
32
+ }
33
+ return { provider: m[1], id: m[2] };
34
+ }
35
+ async function fetchOne(ref, providerName, id) {
36
+ const provider = byName.get(providerName);
37
+ if (!provider) {
38
+ throw new Error(`[ENVX_RUNTIME_UNKNOWN_PROVIDER] '${providerName}' (registered: ${[...byName.keys()].join(", ") || "none"})`);
39
+ }
40
+ // Bypass the plugin's preload-once cache — the runtime owns the
41
+ // TTL cache and needs to be able to refresh on its own schedule.
42
+ // `fetch()` always hits the backend.
43
+ return provider.fetch(id);
44
+ }
45
+ async function load(ref) {
46
+ const { provider, id } = parseRef(ref);
47
+ // Check cached error window first (cache-error mode).
48
+ const errEntry = errors.get(ref);
49
+ if (errEntry && errEntry.expiresAt > now()) {
50
+ throw errEntry.error;
51
+ }
52
+ // Live cache hit.
53
+ const cached = cache.get(ref);
54
+ if (cached && cached.expiresAt > now())
55
+ return cached.value;
56
+ // Single-flight: coalesce concurrent fetches.
57
+ const existing = inflight.get(ref);
58
+ if (existing)
59
+ return existing;
60
+ const promise = (async () => {
61
+ try {
62
+ const value = await fetchOne(ref, provider, id);
63
+ const expiresAt = ttl === Infinity ? Infinity : now() + ttl * 1000;
64
+ cache.set(ref, { value, expiresAt });
65
+ errors.delete(ref);
66
+ return value;
67
+ }
68
+ catch (e) {
69
+ const err = e;
70
+ if (onFailure === "cache-stale" && cached) {
71
+ logger.warn(`envx-runtime: refresh of '${ref}' failed, serving stale value: ${err.message}`);
72
+ // Bump the existing entry so we don't retry on every read; we'll
73
+ // attempt again after the next TTL window.
74
+ const expiresAt = ttl === Infinity ? Infinity : now() + ttl * 1000;
75
+ cache.set(ref, { value: cached.value, expiresAt });
76
+ return cached.value;
77
+ }
78
+ if (onFailure === "cache-error") {
79
+ errors.set(ref, {
80
+ error: err,
81
+ expiresAt: now() + errorTtl * 1000,
82
+ });
83
+ }
84
+ throw err;
85
+ }
86
+ finally {
87
+ inflight.delete(ref);
88
+ }
89
+ })();
90
+ inflight.set(ref, promise);
91
+ return promise;
92
+ }
93
+ return {
94
+ async get(ref) {
95
+ return load(ref);
96
+ },
97
+ async tryGet(ref) {
98
+ try {
99
+ return await load(ref);
100
+ }
101
+ catch {
102
+ return undefined;
103
+ }
104
+ },
105
+ async invalidate(ref) {
106
+ cache.delete(ref);
107
+ errors.delete(ref);
108
+ return load(ref);
109
+ },
110
+ clear() {
111
+ cache.clear();
112
+ errors.clear();
113
+ },
114
+ stats() {
115
+ return {
116
+ cached: cache.size,
117
+ inflight: inflight.size,
118
+ errors: errors.size,
119
+ };
120
+ },
121
+ };
122
+ }
123
+ // #endregion -----------------------------------------------
124
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -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/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"}
package/dist/types.js ADDED
@@ -0,0 +1,64 @@
1
+ // #region -- Plugin Contract -------------------------------
2
+ // #endregion -----------------------------------------------
3
+ // #region -- Common factory helper -------------------------
4
+ /**
5
+ * Internal factory used by every plugin to wire up the preload / resolve
6
+ * / cache trio. Plugin authors hand it a `fetchOne(id)` and we handle
7
+ * the cache + parallelism. Keeps the per-provider files focused on the
8
+ * SDK call.
9
+ */
10
+ export function buildProvider(name, fetchOne) {
11
+ const cache = new Map();
12
+ return {
13
+ name,
14
+ cache,
15
+ fetch: fetchOne,
16
+ async preload(ids) {
17
+ const todo = [...new Set(ids)].filter((id) => !cache.has(id));
18
+ if (todo.length === 0)
19
+ return;
20
+ const results = await Promise.all(todo.map(async (id) => {
21
+ try {
22
+ const value = await fetchOne(id);
23
+ return { id, value, ok: true };
24
+ }
25
+ catch (e) {
26
+ return { id, error: e, ok: false };
27
+ }
28
+ }));
29
+ const errors = [];
30
+ for (const r of results) {
31
+ if (r.ok)
32
+ cache.set(r.id, r.value);
33
+ else
34
+ errors.push({ id: r.id, error: r.error });
35
+ }
36
+ if (errors.length > 0) {
37
+ const summary = errors
38
+ .map((e) => ` ${e.id}: ${e.error.message}`)
39
+ .join("\n");
40
+ throw new Error(`[ENVX_PLUGIN_${name.toUpperCase()}] failed to preload ${String(errors.length)} secret(s):\n${summary}`);
41
+ }
42
+ },
43
+ resolve(id) {
44
+ return cache.get(id);
45
+ },
46
+ };
47
+ }
48
+ // #endregion -----------------------------------------------
49
+ // #region -- Missing-SDK error -----------------------------
50
+ /**
51
+ * Thrown by every plugin's lazy SDK loader when the consumer hasn't
52
+ * installed the relevant SDK package. The message includes the
53
+ * `pnpm add <pkg>` line so users get the exact remediation.
54
+ */
55
+ export class MissingSdkError extends Error {
56
+ constructor(plugin, sdk) {
57
+ super(`[ENVX_PLUGIN_MISSING_SDK] the '${plugin}' plugin needs '${sdk}'.\n` +
58
+ ` install it: pnpm add ${sdk}\n` +
59
+ ` (or your equivalent — npm / yarn / bun)`);
60
+ this.name = "MissingSdkError";
61
+ }
62
+ }
63
+ // #endregion -----------------------------------------------
64
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6DAA6D;AA6C7D,6DAA6D;AAE7D,6DAA6D;AAE7D;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,QAAyC;IAEzC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IAExC,OAAO;QACL,IAAI;QACJ,KAAK;QACL,KAAK,EAAE,QAAQ;QACf,KAAK,CAAC,OAAO,CAAC,GAAsB;YAClC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YAC9D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAC9B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBACpB,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,EAAE,CAAC,CAAC;oBACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAa,EAAE,CAAC;gBAC1C,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAU,EAAE,EAAE,EAAE,KAAc,EAAE,CAAC;gBACvD,CAAC;YACH,CAAC,CAAC,CACH,CAAC;YACF,MAAM,MAAM,GAAwC,EAAE,CAAC;YACvD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,IAAI,CAAC,CAAC,EAAE;oBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;;oBAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACjD,CAAC;YACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,OAAO,GAAG,MAAM;qBACnB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;qBAC3C,IAAI,CAAC,IAAI,CAAC,CAAC;gBACd,MAAM,IAAI,KAAK,CACb,gBAAgB,IAAI,CAAC,WAAW,EAAE,uBAAuB,MAAM,CAC7D,MAAM,CAAC,MAAM,CACd,gBAAgB,OAAO,EAAE,CAC3B,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,CAAC,EAAU;YAChB,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,6DAA6D;AAE7D,6DAA6D;AAE7D;;;;GAIG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,MAAc,EAAE,GAAW;QACrC,KAAK,CACH,kCAAkC,MAAM,mBAAmB,GAAG,MAAM;YAClE,0BAA0B,GAAG,IAAI;YACjC,2CAA2C,CAC9C,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,6DAA6D"}
@@ -0,0 +1,47 @@
1
+ import { type 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/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"}
package/dist/vault.js ADDED
@@ -0,0 +1,63 @@
1
+ import { buildProvider } from "./types.js";
2
+ /**
3
+ * HashiCorp Vault (KV v1 / v2) provider.
4
+ *
5
+ * ```ts
6
+ * import { hcVault } from "@super-repo/envx-plugins/vault";
7
+ *
8
+ * const vault = hcVault({
9
+ * endpoint: process.env.VAULT_ADDR!,
10
+ * token: process.env.VAULT_TOKEN!,
11
+ * mount: "secret",
12
+ * });
13
+ * await vault.preload(["prod/db", "prod/api"]);
14
+ *
15
+ * envx({ resolvers: { [vault.name]: vault.resolve } });
16
+ * ```
17
+ *
18
+ * Reference shape:
19
+ *
20
+ * ```
21
+ * # KV v2 path: <mount>/data/<id>, returns the "value" field by default.
22
+ * # Or pin a specific JSON field: ${vault:prod/db#username}
23
+ * DB_URL=${vault:prod/db}
24
+ * USER=${vault:prod/db#username}
25
+ * ```
26
+ *
27
+ * No SDK install required — uses native `fetch` (Node 20+).
28
+ */
29
+ export function hcVault(opts) {
30
+ const name = opts.name ?? "vault";
31
+ const mount = opts.mount ?? "secret";
32
+ const kvVersion = opts.kvVersion ?? 2;
33
+ const doFetch = opts.fetchImpl ?? fetch;
34
+ return buildProvider(name, async (idWithField) => {
35
+ // `id` may include `#<field>` to pick a specific JSON field.
36
+ const hash = idWithField.indexOf("#");
37
+ const id = hash >= 0 ? idWithField.slice(0, hash) : idWithField;
38
+ const field = hash >= 0 ? idWithField.slice(hash + 1) : "value";
39
+ const segment = kvVersion === 2 ? "data/" : "";
40
+ const url = `${opts.endpoint.replace(/\/$/, "")}/v1/${mount}/${segment}${id}`;
41
+ const r = await doFetch(url, {
42
+ headers: { "X-Vault-Token": opts.token },
43
+ });
44
+ if (!r.ok) {
45
+ throw new Error(`vault GET ${url} → ${String(r.status)} ${r.statusText}`);
46
+ }
47
+ const body = (await r.json());
48
+ // KV v2 nests under data.data; v1 puts it directly under data.
49
+ const data = kvVersion === 2
50
+ ? (body.data
51
+ ?.data ?? {})
52
+ : (body.data ?? {});
53
+ const value = data[field];
54
+ if (typeof value === "string")
55
+ return value;
56
+ if (value === undefined) {
57
+ throw new Error(`vault: '${id}' has no field '${field}' (available: ${Object.keys(data).join(", ") || "none"})`);
58
+ }
59
+ return JSON.stringify(value);
60
+ });
61
+ }
62
+ // #endregion -----------------------------------------------
63
+ //# sourceMappingURL=vault.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.js","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAuB,MAAM,YAAY,CAAC;AAsBhE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,OAAO,CAAC,IAAoB;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAExC,OAAO,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE;QAC/C,6DAA6D;QAC7D,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAEhE,MAAM,OAAO,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QAE9E,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;YAC3B,OAAO,EAAE,EAAE,eAAe,EAAE,IAAI,CAAC,KAAK,EAAE;SACzC,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,aAAa,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAE3B,CAAC;QACF,+DAA+D;QAC/D,MAAM,IAAI,GACR,SAAS,KAAK,CAAC;YACb,CAAC,CAAC,CAAE,IAAI,CAAC,IAAuD;gBAC5D,EAAE,IAAI,IAAI,EAAE,CAAC;YACjB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACxB,MAAM,KAAK,GAAI,IAAgC,CAAC,KAAK,CAAC,CAAC;QACvD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,WAAW,EAAE,mBAAmB,KAAK,iBAAiB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,GAAG,CAChG,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,6DAA6D"}