@microagi/alchemy-gcp 0.1.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.
Files changed (123) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +88 -0
  3. package/lib/Auth/AuthProvider.d.ts +48 -0
  4. package/lib/Auth/AuthProvider.d.ts.map +1 -0
  5. package/lib/Auth/AuthProvider.js +84 -0
  6. package/lib/Auth/AuthProvider.js.map +1 -0
  7. package/lib/Auth/Credentials.d.ts +28 -0
  8. package/lib/Auth/Credentials.d.ts.map +1 -0
  9. package/lib/Auth/Credentials.js +41 -0
  10. package/lib/Auth/Credentials.js.map +1 -0
  11. package/lib/CloudResourceManager/Project.d.ts +143 -0
  12. package/lib/CloudResourceManager/Project.d.ts.map +1 -0
  13. package/lib/CloudResourceManager/Project.js +272 -0
  14. package/lib/CloudResourceManager/Project.js.map +1 -0
  15. package/lib/CloudResourceManager/index.d.ts +3 -0
  16. package/lib/CloudResourceManager/index.d.ts.map +1 -0
  17. package/lib/CloudResourceManager/index.js +2 -0
  18. package/lib/CloudResourceManager/index.js.map +1 -0
  19. package/lib/Compute/GlobalAddress.d.ts +101 -0
  20. package/lib/Compute/GlobalAddress.d.ts.map +1 -0
  21. package/lib/Compute/GlobalAddress.js +134 -0
  22. package/lib/Compute/GlobalAddress.js.map +1 -0
  23. package/lib/Compute/Network.d.ts +114 -0
  24. package/lib/Compute/Network.d.ts.map +1 -0
  25. package/lib/Compute/Network.js +145 -0
  26. package/lib/Compute/Network.js.map +1 -0
  27. package/lib/Compute/Operations.d.ts +34 -0
  28. package/lib/Compute/Operations.d.ts.map +1 -0
  29. package/lib/Compute/Operations.js +62 -0
  30. package/lib/Compute/Operations.js.map +1 -0
  31. package/lib/Compute/SharedVpcHost.d.ts +44 -0
  32. package/lib/Compute/SharedVpcHost.d.ts.map +1 -0
  33. package/lib/Compute/SharedVpcHost.js +71 -0
  34. package/lib/Compute/SharedVpcHost.js.map +1 -0
  35. package/lib/Compute/SharedVpcServiceProject.d.ts +46 -0
  36. package/lib/Compute/SharedVpcServiceProject.d.ts.map +1 -0
  37. package/lib/Compute/SharedVpcServiceProject.js +96 -0
  38. package/lib/Compute/SharedVpcServiceProject.js.map +1 -0
  39. package/lib/Compute/Subnetwork.d.ts +108 -0
  40. package/lib/Compute/Subnetwork.d.ts.map +1 -0
  41. package/lib/Compute/Subnetwork.js +201 -0
  42. package/lib/Compute/Subnetwork.js.map +1 -0
  43. package/lib/Compute/index.d.ts +6 -0
  44. package/lib/Compute/index.d.ts.map +1 -0
  45. package/lib/Compute/index.js +6 -0
  46. package/lib/Compute/index.js.map +1 -0
  47. package/lib/Container/Cluster.d.ts +216 -0
  48. package/lib/Container/Cluster.d.ts.map +1 -0
  49. package/lib/Container/Cluster.js +326 -0
  50. package/lib/Container/Cluster.js.map +1 -0
  51. package/lib/Container/NodePool.d.ts +214 -0
  52. package/lib/Container/NodePool.d.ts.map +1 -0
  53. package/lib/Container/NodePool.js +344 -0
  54. package/lib/Container/NodePool.js.map +1 -0
  55. package/lib/Container/Operations.d.ts +50 -0
  56. package/lib/Container/Operations.d.ts.map +1 -0
  57. package/lib/Container/Operations.js +51 -0
  58. package/lib/Container/Operations.js.map +1 -0
  59. package/lib/Container/index.d.ts +5 -0
  60. package/lib/Container/index.d.ts.map +1 -0
  61. package/lib/Container/index.js +3 -0
  62. package/lib/Container/index.js.map +1 -0
  63. package/lib/Parallelstore/Instance.d.ts +119 -0
  64. package/lib/Parallelstore/Instance.d.ts.map +1 -0
  65. package/lib/Parallelstore/Instance.js +180 -0
  66. package/lib/Parallelstore/Instance.js.map +1 -0
  67. package/lib/Parallelstore/index.d.ts +2 -0
  68. package/lib/Parallelstore/index.d.ts.map +1 -0
  69. package/lib/Parallelstore/index.js +2 -0
  70. package/lib/Parallelstore/index.js.map +1 -0
  71. package/lib/Providers.d.ts +24 -0
  72. package/lib/Providers.d.ts.map +1 -0
  73. package/lib/Providers.js +51 -0
  74. package/lib/Providers.js.map +1 -0
  75. package/lib/ServiceNetworking/PsaConnection.d.ts +84 -0
  76. package/lib/ServiceNetworking/PsaConnection.d.ts.map +1 -0
  77. package/lib/ServiceNetworking/PsaConnection.js +130 -0
  78. package/lib/ServiceNetworking/PsaConnection.js.map +1 -0
  79. package/lib/ServiceNetworking/index.d.ts +2 -0
  80. package/lib/ServiceNetworking/index.d.ts.map +1 -0
  81. package/lib/ServiceNetworking/index.js +2 -0
  82. package/lib/ServiceNetworking/index.js.map +1 -0
  83. package/lib/ServiceUsage/ApiEnable.d.ts +78 -0
  84. package/lib/ServiceUsage/ApiEnable.d.ts.map +1 -0
  85. package/lib/ServiceUsage/ApiEnable.js +132 -0
  86. package/lib/ServiceUsage/ApiEnable.js.map +1 -0
  87. package/lib/ServiceUsage/index.d.ts +3 -0
  88. package/lib/ServiceUsage/index.d.ts.map +1 -0
  89. package/lib/ServiceUsage/index.js +2 -0
  90. package/lib/ServiceUsage/index.js.map +1 -0
  91. package/lib/Tags.d.ts +57 -0
  92. package/lib/Tags.d.ts.map +1 -0
  93. package/lib/Tags.js +106 -0
  94. package/lib/Tags.js.map +1 -0
  95. package/lib/index.d.ts +11 -0
  96. package/lib/index.d.ts.map +1 -0
  97. package/lib/index.js +10 -0
  98. package/lib/index.js.map +1 -0
  99. package/package.json +70 -0
  100. package/src/Auth/AuthProvider.ts +180 -0
  101. package/src/Auth/Credentials.ts +69 -0
  102. package/src/CloudResourceManager/Project.ts +517 -0
  103. package/src/CloudResourceManager/index.ts +2 -0
  104. package/src/Compute/GlobalAddress.ts +283 -0
  105. package/src/Compute/Network.ts +306 -0
  106. package/src/Compute/Operations.ts +108 -0
  107. package/src/Compute/SharedVpcHost.ts +141 -0
  108. package/src/Compute/SharedVpcServiceProject.ts +185 -0
  109. package/src/Compute/Subnetwork.ts +375 -0
  110. package/src/Compute/index.ts +5 -0
  111. package/src/Container/Cluster.ts +599 -0
  112. package/src/Container/NodePool.ts +620 -0
  113. package/src/Container/Operations.ts +88 -0
  114. package/src/Container/index.ts +8 -0
  115. package/src/Parallelstore/Instance.ts +371 -0
  116. package/src/Parallelstore/index.ts +1 -0
  117. package/src/Providers.ts +84 -0
  118. package/src/ServiceNetworking/PsaConnection.ts +269 -0
  119. package/src/ServiceNetworking/index.ts +1 -0
  120. package/src/ServiceUsage/ApiEnable.ts +267 -0
  121. package/src/ServiceUsage/index.ts +2 -0
  122. package/src/Tags.ts +130 -0
  123. package/src/index.ts +16 -0
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@microagi/alchemy-gcp",
3
+ "version": "0.1.2",
4
+ "description": "GCP provider for Alchemy v2 (Effect-based IaC). Resources for Project, Cluster, NodePool, Parallelstore, Compute, ServiceUsage, ServiceNetworking, with ADC-backed credentials.",
5
+ "keywords": [
6
+ "alchemy",
7
+ "alchemy-effect",
8
+ "iac",
9
+ "infrastructure",
10
+ "effect",
11
+ "effect-ts",
12
+ "gcp",
13
+ "google-cloud",
14
+ "gke",
15
+ "kubernetes",
16
+ "parallelstore",
17
+ "distilled.cloud"
18
+ ],
19
+ "homepage": "https://github.com/MicroAGI-Labs/alchemy-v2-gcp#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/MicroAGI-Labs/alchemy-v2-gcp/issues"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/MicroAGI-Labs/alchemy-v2-gcp.git"
26
+ },
27
+ "license": "Apache-2.0",
28
+ "author": "MicroAGI Labs",
29
+ "type": "module",
30
+ "sideEffects": false,
31
+ "files": [
32
+ "lib",
33
+ "src",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "exports": {
38
+ ".": {
39
+ "types": "./lib/index.d.ts",
40
+ "bun": "./src/index.ts",
41
+ "import": "./lib/index.js"
42
+ }
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.build.json",
46
+ "clean": "rm -rf lib",
47
+ "test": "CI=1 bun test",
48
+ "test:watch": "CI=1 bun test --watch",
49
+ "prepublishOnly": "bun run clean && bun run build"
50
+ },
51
+ "dependencies": {
52
+ "google-auth-library": "^10.4.0"
53
+ },
54
+ "peerDependencies": {
55
+ "@distilled.cloud/gcp": ">=0.16.9",
56
+ "alchemy": ">=2.0.0-beta.30",
57
+ "effect": ">=4.0.0-beta.60 || >=4.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@distilled.cloud/gcp": "workspace:*",
61
+ "@types/bun": "latest",
62
+ "@types/node": "latest",
63
+ "alchemy": "workspace:*",
64
+ "effect": ">=4.0.0-beta.60 || >=4.0.0",
65
+ "typescript": "^6.0.3"
66
+ },
67
+ "publishConfig": {
68
+ "access": "public"
69
+ }
70
+ }
@@ -0,0 +1,180 @@
1
+ import {
2
+ AuthError,
3
+ AuthProviderLayer,
4
+ type ConfigureContext,
5
+ } from "alchemy/Auth/AuthProvider";
6
+ import { displayRedacted } from "alchemy/Auth/Credentials";
7
+ import { retryOnce } from "alchemy/Auth/Env";
8
+ import * as Clank from "alchemy/Util/Clank";
9
+ import * as Console from "effect/Console";
10
+ import * as Effect from "effect/Effect";
11
+ import * as Match from "effect/Match";
12
+ import * as Redacted from "effect/Redacted";
13
+ import { GoogleAuth } from "google-auth-library";
14
+
15
+ const SCOPES = ["https://www.googleapis.com/auth/cloud-platform"];
16
+
17
+ export const GCP_AUTH_PROVIDER_NAME = "GCP";
18
+
19
+ /**
20
+ * GCP profile config.
21
+ *
22
+ * There is intentionally a single method — `adc` — because
23
+ * `google-auth-library` already discovers every credential source we
24
+ * care about, in this priority order:
25
+ *
26
+ * 1. `GOOGLE_APPLICATION_CREDENTIALS` env var → JSON service-account key
27
+ * 2. gcloud user ADC at `~/.config/gcloud/application_default_credentials.json`
28
+ * 3. GCE/GKE metadata server (workload identity)
29
+ *
30
+ * To pin a specific key file at the *profile* level (rather than
31
+ * process-wide via the env var), set `keyFile`. To override the
32
+ * billing/quota project, set `project`. Both are passed straight
33
+ * through to `new GoogleAuth({ ... })`.
34
+ */
35
+ export type GCPAuthConfig = {
36
+ method: "adc";
37
+ /** Override the project that google-auth-library would discover. */
38
+ project?: string;
39
+ /** Pin a specific service-account JSON key file to this profile. */
40
+ keyFile?: string;
41
+ };
42
+
43
+ export interface GCPResolvedCredentials {
44
+ accessToken: Redacted.Redacted<string>;
45
+ project: string | undefined;
46
+ }
47
+
48
+ /**
49
+ * Mint a fresh access token (and resolve a project) by delegating to
50
+ * `google-auth-library`. All credential discovery — env-var key files,
51
+ * gcloud user ADC, metadata server — is the library's job; we just
52
+ * pass through `project` / `keyFile` overrides.
53
+ */
54
+ export const mintToken = (
55
+ config: { project?: string; keyFile?: string } = {},
56
+ ): Effect.Effect<GCPResolvedCredentials, AuthError> =>
57
+ Effect.gen(function* () {
58
+ const auth = yield* Effect.sync(
59
+ () =>
60
+ new GoogleAuth({
61
+ scopes: SCOPES,
62
+ ...(config.keyFile ? { keyFile: config.keyFile } : {}),
63
+ ...(config.project ? { projectId: config.project } : {}),
64
+ }),
65
+ );
66
+ const accessToken = yield* Effect.tryPromise({
67
+ try: () => auth.getAccessToken(),
68
+ catch: (cause) =>
69
+ new AuthError({
70
+ message: `Failed to acquire GCP access token: ${
71
+ (cause as { message?: string })?.message ?? String(cause)
72
+ }. Run \`gcloud auth application-default login\` or set GOOGLE_APPLICATION_CREDENTIALS.`,
73
+ cause,
74
+ }),
75
+ });
76
+ if (!accessToken) {
77
+ return yield* new AuthError({
78
+ message:
79
+ "google-auth-library returned an empty access token. Run `gcloud auth application-default login` or set GOOGLE_APPLICATION_CREDENTIALS.",
80
+ });
81
+ }
82
+ const project = yield* Effect.tryPromise({
83
+ try: () => auth.getProjectId(),
84
+ catch: () => undefined,
85
+ }).pipe(Effect.orElseSucceed<string | undefined>(() => undefined));
86
+ return {
87
+ accessToken: Redacted.make(accessToken),
88
+ project: project ?? undefined,
89
+ };
90
+ });
91
+
92
+ const configureCredentials = (
93
+ _profileName: string,
94
+ ctx: ConfigureContext,
95
+ ): Effect.Effect<GCPAuthConfig, AuthError> =>
96
+ Effect.gen(function* () {
97
+ if (ctx.ci) return { method: "adc" as const };
98
+
99
+ // Validate that google-auth-library can resolve credentials. If it
100
+ // can't, surface a hint but don't block — the user might be about
101
+ // to fix their environment, and `read` will fail loudly later.
102
+ yield* mintToken().pipe(
103
+ Effect.tap(() => Clank.success("GCP: ADC credentials available.")),
104
+ Effect.catch((e) =>
105
+ Clank.warn(
106
+ `GCP: ${e.message} — continuing; alchemy will retry when credentials are needed.`,
107
+ ).pipe(Effect.asVoid),
108
+ ),
109
+ );
110
+
111
+ const project = yield* Clank.text({
112
+ message:
113
+ "Default GCP project (Enter to defer to google-auth-library discovery)",
114
+ placeholder:
115
+ process.env.GOOGLE_CLOUD_PROJECT ??
116
+ process.env.GOOGLE_PROJECT_ID ??
117
+ "",
118
+ defaultValue: "",
119
+ }).pipe(
120
+ retryOnce,
121
+ Effect.catch(() => Effect.succeed("")),
122
+ );
123
+
124
+ return {
125
+ method: "adc" as const,
126
+ ...(project.trim() ? { project: project.trim() } : {}),
127
+ };
128
+ });
129
+
130
+ const login = (_profileName: string, config: GCPAuthConfig) =>
131
+ mintToken(config).pipe(
132
+ Effect.tap(() => Clank.success("GCP: credentials valid.")),
133
+ Effect.asVoid,
134
+ );
135
+
136
+ const logout = (_profileName: string, _config: GCPAuthConfig) =>
137
+ Clank.info(
138
+ "GCP: credentials are managed by google-auth-library. Run `gcloud auth application-default revoke` to revoke user ADC, or unset GOOGLE_APPLICATION_CREDENTIALS to drop a service-account key.",
139
+ );
140
+
141
+ const resolveCredentials = (
142
+ _profileName: string,
143
+ config: GCPAuthConfig,
144
+ ): Effect.Effect<GCPResolvedCredentials, AuthError> =>
145
+ Match.value(config).pipe(
146
+ Match.when({ method: "adc" }, (cfg) => mintToken(cfg)),
147
+ Match.exhaustive,
148
+ );
149
+
150
+ const prettyPrint = (profileName: string, config: GCPAuthConfig) =>
151
+ resolveCredentials(profileName, config).pipe(
152
+ Effect.tap((creds) =>
153
+ Effect.all([
154
+ Console.log(` accessToken: ${displayRedacted(creds.accessToken, 6)}`),
155
+ Console.log(` project: ${creds.project ?? "(none)"}`),
156
+ Console.log(
157
+ ` source: google-auth-library${config.keyFile ? ` (keyFile=${config.keyFile})` : config.project ? " (ADC + pinned project)" : " (ADC)"}`,
158
+ ),
159
+ ]),
160
+ ),
161
+ Effect.catch((e) =>
162
+ Console.error(` Failed to retrieve credentials: ${e}`),
163
+ ),
164
+ );
165
+
166
+ /**
167
+ * Layer that registers the GCP {@link AuthProvider} into the
168
+ * {@link AuthProviders} registry when built. Include this in the GCP
169
+ * `providers()` layer so `alchemy login` can discover it.
170
+ */
171
+ export const GCPAuth = AuthProviderLayer<
172
+ GCPAuthConfig,
173
+ GCPResolvedCredentials
174
+ >()(GCP_AUTH_PROVIDER_NAME, {
175
+ configure: configureCredentials,
176
+ login,
177
+ logout,
178
+ prettyPrint,
179
+ read: resolveCredentials,
180
+ });
@@ -0,0 +1,69 @@
1
+ import { ConfigError, Credentials } from "@distilled.cloud/gcp";
2
+ import { getAuthProvider } from "alchemy/Auth/AuthProvider";
3
+ import { ALCHEMY_PROFILE, loadOrConfigure } from "alchemy/Auth/Profile";
4
+ import * as Config from "effect/Config";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Layer from "effect/Layer";
7
+ import {
8
+ GCP_AUTH_PROVIDER_NAME,
9
+ type GCPAuthConfig,
10
+ type GCPResolvedCredentials,
11
+ mintToken,
12
+ } from "./AuthProvider.ts";
13
+
14
+ export { Credentials } from "@distilled.cloud/gcp";
15
+
16
+ const toConfigError = (e: { message?: string } | unknown) =>
17
+ new ConfigError({
18
+ message: `Failed to resolve GCP credentials: ${
19
+ (e as { message?: string })?.message ?? String(e)
20
+ }`,
21
+ });
22
+
23
+ /**
24
+ * Single-profile ADC bridge. Bypasses the `AuthProvider` registry —
25
+ * useful for tests and one-off scripts where multi-profile resolution
26
+ * isn't needed. All credential discovery is delegated to
27
+ * `google-auth-library`; the optional `project` argument overrides
28
+ * the project the library would discover.
29
+ *
30
+ * Prefer {@link fromAuthProvider} when the stack runs through
31
+ * `alchemy login` / `ALCHEMY_PROFILE`.
32
+ */
33
+ export const fromADC = (project?: string) =>
34
+ Layer.effect(
35
+ Credentials,
36
+ mintToken({ project }).pipe(Effect.mapError(toConfigError)),
37
+ );
38
+
39
+ /**
40
+ * Build a `Credentials` Layer that resolves GCP credentials via the
41
+ * Alchemy AuthProvider registry, using the active profile (from
42
+ * `ALCHEMY_PROFILE`, default `"default"`).
43
+ *
44
+ * Routes through `loadOrConfigure` so a missing profile triggers the
45
+ * provider's `configure` step (interactive locally, `{ method: "adc" }`
46
+ * in CI) before reading credentials. Both methods of the provider
47
+ * ultimately call `mintToken`, which is just a thin wrapper over
48
+ * `google-auth-library`.
49
+ */
50
+ export const fromAuthProvider = () =>
51
+ Layer.effect(
52
+ Credentials,
53
+ Effect.gen(function* () {
54
+ const auth = yield* getAuthProvider<
55
+ GCPAuthConfig,
56
+ GCPResolvedCredentials
57
+ >(GCP_AUTH_PROVIDER_NAME);
58
+ const profileName = yield* ALCHEMY_PROFILE;
59
+ const ci = yield* Config.boolean("CI").pipe(Config.withDefault(false));
60
+ const ctx = yield* Effect.context<never>();
61
+
62
+ return yield* loadOrConfigure(auth, profileName, { ci }).pipe(
63
+ Effect.flatMap((cfg) => auth.read(profileName, cfg as GCPAuthConfig)),
64
+ Effect.map(({ accessToken, project }) => ({ accessToken, project })),
65
+ Effect.mapError(toConfigError),
66
+ Effect.provide(ctx),
67
+ );
68
+ }),
69
+ );