@webpieces/gcp-identity 0.3.380 → 0.3.382

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 CHANGED
@@ -5,12 +5,23 @@ GCP metadata server / ADC at runtime — nothing is configured. Off-GCP (local d
5
5
  tests) every call falls back to a deterministic localhost value so no GCP is needed.
6
6
 
7
7
  - `getProjectId()` / `getNumericProjectId()` / `getRegion()` — cached metadata lookups
8
- - `getServiceName()` — logical name from `K_SERVICE` (strips a `tf-` prefix), else `'local'`
8
+ - `getServiceName()` — this service's name from `K_SERVICE`, verbatim, else `'local'`
9
9
  - `getSelfCloudRunUrl()` — this service's own base URL
10
- - `resolveServiceUrl(svcName)` — the base URL to call svcName: a `ClientRegistry` override wins, else the Cloud Run URL is derived on GCP, else (off-GCP, unregistered) it throws
10
+ - `gcpCloudRunDeriver()` — the GCP half of URL resolution: `svcName` `https://<svc>-<projectNumber>.<region>.run.app`. Install it once at startup with `ClientRegistry.setDeriver(gcpCloudRunDeriver())` and every same-project/same-region peer resolves with no URL table. Off GCP (a CLI, CI) pass the values instead: `gcpCloudRunDeriver(new GcpCloudRunTarget(projectNumber, region))`
11
11
  - `getRuntimeServiceAccountEmail()` — the SA this process runs as
12
12
  - `mintIdToken(audience)` — Google-signed OIDC ID token (a `dev-oidc.*` token off-GCP)
13
13
  - `verifyOidcFromCallers(idToken, callers)` — verify + allow-list the caller SA
14
14
 
15
15
  Underpins the `@AuthOidc` service-to-service auth mode enforced by `ServiceAuthFilter`
16
16
  and used by `@webpieces/http-client` (RPC) and `@webpieces/cloudtasks-client`.
17
+
18
+ **There is exactly ONE service name.** The Cloud Run service name is what you report, what peers
19
+ call you by, and what goes in every URL — yours and theirs. Nothing strips or adds a prefix: deploy
20
+ a service as `tf-server2` and its `svcName` is `tf-server2`. (`getServiceName()` used to strip a
21
+ leading `tf-`, which made that service unreachable by the very name it reported.)
22
+
23
+ **URL resolution itself does not live here** — it lives in `ClientRegistry`
24
+ (`@webpieces/core-util`, browser-safe), which runs one chain for every client: a registered mapping,
25
+ else the installed deriver, else the caller's fallback. This package only supplies the GCP deriver.
26
+ That is the seam: an AWS deployment installs `templateDeriver` (or just registers mappings) and
27
+ never pulls `gcp-metadata` onto the URL path.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/gcp-identity",
3
- "version": "0.3.380",
3
+ "version": "0.3.382",
4
4
  "description": "GCP runtime identity: project/region metadata, Cloud Run URLs, OIDC mint/verify",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -23,8 +23,8 @@
23
23
  "access": "public"
24
24
  },
25
25
  "dependencies": {
26
- "@webpieces/core-context": "0.3.380",
27
- "@webpieces/core-util": "0.3.380",
26
+ "@webpieces/core-context": "0.3.382",
27
+ "@webpieces/core-util": "0.3.382",
28
28
  "google-auth-library": "9.15.1",
29
29
  "gcp-metadata": "6.1.1",
30
30
  "inversify": "7.10.4",
@@ -0,0 +1,37 @@
1
+ import { ServiceUrlDeriver } from '@webpieces/core-util';
2
+ /**
3
+ * Where the Cloud Run services being called live, for code that is NOT itself on GCP and therefore
4
+ * has no metadata server to read them from — a CLI on a laptop, a CI job, a test.
5
+ *
6
+ * On GCP you never construct this: `gcpCloudRunDeriver()` reads both values from the metadata server.
7
+ */
8
+ export declare class GcpCloudRunTarget {
9
+ /** The NUMERIC project id (not the project id string) — Cloud Run URLs are built from it. */
10
+ readonly projectNumber: string;
11
+ /** e.g. 'us-central1'. */
12
+ readonly region: string;
13
+ constructor(
14
+ /** The NUMERIC project id (not the project id string) — Cloud Run URLs are built from it. */
15
+ projectNumber: string,
16
+ /** e.g. 'us-central1'. */
17
+ region: string);
18
+ }
19
+ /**
20
+ * The GCP {@link ServiceUrlDeriver}: `svcName` -> `https://<svc>-<projectNumber>.<region>.run.app`.
21
+ *
22
+ * That derived form is a live alias of Cloud Run's hash URL (`<svc>-5s4met7cnq-uc.a.run.app`), so
23
+ * one formula reaches every service in the project+region and you maintain NO url table across
24
+ * demo/qa/prod. `svcName` is the CLOUD RUN SERVICE NAME, verbatim — if you deploy a service as
25
+ * `tf-server2` then its svcName is `tf-server2`. Nothing here strips or adds a prefix.
26
+ *
27
+ * Install it once at startup; it only ever runs for a svcName with no {@link ClientRegistry} mapping:
28
+ *
29
+ * ```ts
30
+ * ClientRegistry.setDeriver(gcpCloudRunDeriver()); // ON GCP
31
+ * ClientRegistry.setDeriver(gcpCloudRunDeriver(new GcpCloudRunTarget('851991', 'us-central1'))); // a CLI
32
+ * ```
33
+ *
34
+ * @param target supply it OFF GCP (where there is no metadata server, but the URL is still
35
+ * deterministic); omit it ON GCP to read project+region from the metadata server.
36
+ */
37
+ export declare function gcpCloudRunDeriver(target?: GcpCloudRunTarget): ServiceUrlDeriver;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GcpCloudRunTarget = void 0;
4
+ exports.gcpCloudRunDeriver = gcpCloudRunDeriver;
5
+ const metadata_1 = require("./metadata");
6
+ /**
7
+ * Where the Cloud Run services being called live, for code that is NOT itself on GCP and therefore
8
+ * has no metadata server to read them from — a CLI on a laptop, a CI job, a test.
9
+ *
10
+ * On GCP you never construct this: `gcpCloudRunDeriver()` reads both values from the metadata server.
11
+ */
12
+ class GcpCloudRunTarget {
13
+ projectNumber;
14
+ region;
15
+ constructor(
16
+ /** The NUMERIC project id (not the project id string) — Cloud Run URLs are built from it. */
17
+ projectNumber,
18
+ /** e.g. 'us-central1'. */
19
+ region) {
20
+ this.projectNumber = projectNumber;
21
+ this.region = region;
22
+ }
23
+ }
24
+ exports.GcpCloudRunTarget = GcpCloudRunTarget;
25
+ /**
26
+ * The GCP {@link ServiceUrlDeriver}: `svcName` -> `https://<svc>-<projectNumber>.<region>.run.app`.
27
+ *
28
+ * That derived form is a live alias of Cloud Run's hash URL (`<svc>-5s4met7cnq-uc.a.run.app`), so
29
+ * one formula reaches every service in the project+region and you maintain NO url table across
30
+ * demo/qa/prod. `svcName` is the CLOUD RUN SERVICE NAME, verbatim — if you deploy a service as
31
+ * `tf-server2` then its svcName is `tf-server2`. Nothing here strips or adds a prefix.
32
+ *
33
+ * Install it once at startup; it only ever runs for a svcName with no {@link ClientRegistry} mapping:
34
+ *
35
+ * ```ts
36
+ * ClientRegistry.setDeriver(gcpCloudRunDeriver()); // ON GCP
37
+ * ClientRegistry.setDeriver(gcpCloudRunDeriver(new GcpCloudRunTarget('851991', 'us-central1'))); // a CLI
38
+ * ```
39
+ *
40
+ * @param target supply it OFF GCP (where there is no metadata server, but the URL is still
41
+ * deterministic); omit it ON GCP to read project+region from the metadata server.
42
+ */
43
+ // webpieces-disable no-function-outside-class -- a deriver IS a function; this is the factory that closes over the target (see ServiceUrlDeriver)
44
+ function gcpCloudRunDeriver(target) {
45
+ return async (svcName) => {
46
+ const resolved = target ?? (await readTargetFromMetadata(svcName));
47
+ return `https://${svcName}-${resolved.projectNumber}.${resolved.region}.run.app`;
48
+ };
49
+ }
50
+ /** Project + region off the metadata server. Every read is memoized, so only the first call pays. */
51
+ // webpieces-disable no-function-outside-class -- module-private helper of the deriver factory above
52
+ async function readTargetFromMetadata(svcName) {
53
+ if (!(await (0, metadata_1.isOnGcp)())) {
54
+ throw new Error(`Cannot derive a Cloud Run URL for "${svcName}": this process is NOT on GCP, so there is no ` +
55
+ `metadata server to read the project number and region from. Off GCP, either supply them — ` +
56
+ `ClientRegistry.setDeriver(gcpCloudRunDeriver(new GcpCloudRunTarget(projectNumber, region))) — ` +
57
+ `or register the URL: ClientRegistry.addUrlMapping('${svcName}', 'https://...').`);
58
+ }
59
+ const projectNumber = await (0, metadata_1.readNumericProjectId)();
60
+ const region = await (0, metadata_1.readRegion)();
61
+ return new GcpCloudRunTarget(projectNumber, region);
62
+ }
63
+ //# sourceMappingURL=gcpCloudRunDeriver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcpCloudRunDeriver.js","sourceRoot":"","sources":["../../../../../packages/cloud/gcp-identity/src/gcpCloudRunDeriver.ts"],"names":[],"mappings":";;;AAqCA,gDAKC;AAzCD,yCAAuE;AAEvE;;;;;GAKG;AACH,MAAa,iBAAiB;IAGN;IAEA;IAJpB;IACI,6FAA6F;IAC7E,aAAqB;IACrC,0BAA0B;IACV,MAAc;QAFd,kBAAa,GAAb,aAAa,CAAQ;QAErB,WAAM,GAAN,MAAM,CAAQ;IAC/B,CAAC;CACP;AAPD,8CAOC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,kJAAkJ;AAClJ,SAAgB,kBAAkB,CAAC,MAA0B;IACzD,OAAO,KAAK,EAAE,OAAe,EAAmB,EAAE;QAC9C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;QACnE,OAAO,WAAW,OAAO,IAAI,QAAQ,CAAC,aAAa,IAAI,QAAQ,CAAC,MAAM,UAAU,CAAC;IACrF,CAAC,CAAC;AACN,CAAC;AAED,qGAAqG;AACrG,oGAAoG;AACpG,KAAK,UAAU,sBAAsB,CAAC,OAAe;IACjD,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACX,sCAAsC,OAAO,gDAAgD;YAC7F,4FAA4F;YAC5F,gGAAgG;YAChG,sDAAsD,OAAO,oBAAoB,CACpF,CAAC;IACN,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,IAAA,+BAAoB,GAAE,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAU,GAAE,CAAC;IAClC,OAAO,IAAI,iBAAiB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;AACxD,CAAC","sourcesContent":["import { ServiceUrlDeriver } from '@webpieces/core-util';\nimport { isOnGcp, readNumericProjectId, readRegion } from './metadata';\n\n/**\n * Where the Cloud Run services being called live, for code that is NOT itself on GCP and therefore\n * has no metadata server to read them from — a CLI on a laptop, a CI job, a test.\n *\n * On GCP you never construct this: `gcpCloudRunDeriver()` reads both values from the metadata server.\n */\nexport class GcpCloudRunTarget {\n constructor(\n /** The NUMERIC project id (not the project id string) — Cloud Run URLs are built from it. */\n public readonly projectNumber: string,\n /** e.g. 'us-central1'. */\n public readonly region: string,\n ) {}\n}\n\n/**\n * The GCP {@link ServiceUrlDeriver}: `svcName` -> `https://<svc>-<projectNumber>.<region>.run.app`.\n *\n * That derived form is a live alias of Cloud Run's hash URL (`<svc>-5s4met7cnq-uc.a.run.app`), so\n * one formula reaches every service in the project+region and you maintain NO url table across\n * demo/qa/prod. `svcName` is the CLOUD RUN SERVICE NAME, verbatim — if you deploy a service as\n * `tf-server2` then its svcName is `tf-server2`. Nothing here strips or adds a prefix.\n *\n * Install it once at startup; it only ever runs for a svcName with no {@link ClientRegistry} mapping:\n *\n * ```ts\n * ClientRegistry.setDeriver(gcpCloudRunDeriver()); // ON GCP\n * ClientRegistry.setDeriver(gcpCloudRunDeriver(new GcpCloudRunTarget('851991', 'us-central1'))); // a CLI\n * ```\n *\n * @param target supply it OFF GCP (where there is no metadata server, but the URL is still\n * deterministic); omit it ON GCP to read project+region from the metadata server.\n */\n// webpieces-disable no-function-outside-class -- a deriver IS a function; this is the factory that closes over the target (see ServiceUrlDeriver)\nexport function gcpCloudRunDeriver(target?: GcpCloudRunTarget): ServiceUrlDeriver {\n return async (svcName: string): Promise<string> => {\n const resolved = target ?? (await readTargetFromMetadata(svcName));\n return `https://${svcName}-${resolved.projectNumber}.${resolved.region}.run.app`;\n };\n}\n\n/** Project + region off the metadata server. Every read is memoized, so only the first call pays. */\n// webpieces-disable no-function-outside-class -- module-private helper of the deriver factory above\nasync function readTargetFromMetadata(svcName: string): Promise<GcpCloudRunTarget> {\n if (!(await isOnGcp())) {\n throw new Error(\n `Cannot derive a Cloud Run URL for \"${svcName}\": this process is NOT on GCP, so there is no ` +\n `metadata server to read the project number and region from. Off GCP, either supply them — ` +\n `ClientRegistry.setDeriver(gcpCloudRunDeriver(new GcpCloudRunTarget(projectNumber, region))) — ` +\n `or register the URL: ClientRegistry.addUrlMapping('${svcName}', 'https://...').`,\n );\n }\n const projectNumber = await readNumericProjectId();\n const region = await readRegion();\n return new GcpCloudRunTarget(projectNumber, region);\n}\n"]}
package/src/index.d.ts CHANGED
@@ -7,7 +7,13 @@
7
7
  *
8
8
  * Underpins the @AuthOidc service-to-service auth mode (enforced by the framework AuthFilter
9
9
  * via an app-bound AuthConfig) and the RPC + Cloud Tasks clients.
10
+ *
11
+ * URL RESOLUTION lives in core-util's browser-safe `ClientRegistry`, NOT here; this package
12
+ * contributes only the GCP half of it — {@link gcpCloudRunDeriver}, which an app installs with
13
+ * `ClientRegistry.setDeriver(...)` at startup. That is the seam that lets a non-GCP deployment use
14
+ * `templateDeriver` (or plain mappings) and never pull gcp-metadata onto the URL path.
10
15
  */
11
- export { isOnGcp } from './metadata';
12
- export { getServiceName, getProjectId, getRegion, getRuntimeServiceAccountEmail, getSelfCloudRunUrl, resolveServiceUrl, LOCAL_SERVICE_ACCOUNT_EMAIL, } from './urls';
16
+ export { isOnGcp, resetMetadataForTests } from './metadata';
17
+ export { getServiceName, getProjectId, getRegion, getRuntimeServiceAccountEmail, getSelfCloudRunUrl, LOCAL_SERVICE_ACCOUNT_EMAIL, } from './urls';
18
+ export { gcpCloudRunDeriver, GcpCloudRunTarget } from './gcpCloudRunDeriver';
13
19
  export { GcpOidc, OidcVerifyResult } from './oidc';
package/src/index.js CHANGED
@@ -8,19 +8,27 @@
8
8
  *
9
9
  * Underpins the @AuthOidc service-to-service auth mode (enforced by the framework AuthFilter
10
10
  * via an app-bound AuthConfig) and the RPC + Cloud Tasks clients.
11
+ *
12
+ * URL RESOLUTION lives in core-util's browser-safe `ClientRegistry`, NOT here; this package
13
+ * contributes only the GCP half of it — {@link gcpCloudRunDeriver}, which an app installs with
14
+ * `ClientRegistry.setDeriver(...)` at startup. That is the seam that lets a non-GCP deployment use
15
+ * `templateDeriver` (or plain mappings) and never pull gcp-metadata onto the URL path.
11
16
  */
12
17
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.OidcVerifyResult = exports.GcpOidc = exports.LOCAL_SERVICE_ACCOUNT_EMAIL = exports.resolveServiceUrl = exports.getSelfCloudRunUrl = exports.getRuntimeServiceAccountEmail = exports.getRegion = exports.getProjectId = exports.getServiceName = exports.isOnGcp = void 0;
18
+ exports.OidcVerifyResult = exports.GcpOidc = exports.GcpCloudRunTarget = exports.gcpCloudRunDeriver = exports.LOCAL_SERVICE_ACCOUNT_EMAIL = exports.getSelfCloudRunUrl = exports.getRuntimeServiceAccountEmail = exports.getRegion = exports.getProjectId = exports.getServiceName = exports.resetMetadataForTests = exports.isOnGcp = void 0;
14
19
  var metadata_1 = require("./metadata");
15
20
  Object.defineProperty(exports, "isOnGcp", { enumerable: true, get: function () { return metadata_1.isOnGcp; } });
21
+ Object.defineProperty(exports, "resetMetadataForTests", { enumerable: true, get: function () { return metadata_1.resetMetadataForTests; } });
16
22
  var urls_1 = require("./urls");
17
23
  Object.defineProperty(exports, "getServiceName", { enumerable: true, get: function () { return urls_1.getServiceName; } });
18
24
  Object.defineProperty(exports, "getProjectId", { enumerable: true, get: function () { return urls_1.getProjectId; } });
19
25
  Object.defineProperty(exports, "getRegion", { enumerable: true, get: function () { return urls_1.getRegion; } });
20
26
  Object.defineProperty(exports, "getRuntimeServiceAccountEmail", { enumerable: true, get: function () { return urls_1.getRuntimeServiceAccountEmail; } });
21
27
  Object.defineProperty(exports, "getSelfCloudRunUrl", { enumerable: true, get: function () { return urls_1.getSelfCloudRunUrl; } });
22
- Object.defineProperty(exports, "resolveServiceUrl", { enumerable: true, get: function () { return urls_1.resolveServiceUrl; } });
23
28
  Object.defineProperty(exports, "LOCAL_SERVICE_ACCOUNT_EMAIL", { enumerable: true, get: function () { return urls_1.LOCAL_SERVICE_ACCOUNT_EMAIL; } });
29
+ var gcpCloudRunDeriver_1 = require("./gcpCloudRunDeriver");
30
+ Object.defineProperty(exports, "gcpCloudRunDeriver", { enumerable: true, get: function () { return gcpCloudRunDeriver_1.gcpCloudRunDeriver; } });
31
+ Object.defineProperty(exports, "GcpCloudRunTarget", { enumerable: true, get: function () { return gcpCloudRunDeriver_1.GcpCloudRunTarget; } });
24
32
  var oidc_1 = require("./oidc");
25
33
  Object.defineProperty(exports, "GcpOidc", { enumerable: true, get: function () { return oidc_1.GcpOidc; } });
26
34
  Object.defineProperty(exports, "OidcVerifyResult", { enumerable: true, get: function () { return oidc_1.OidcVerifyResult; } });
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/cloud/gcp-identity/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,uCAAqC;AAA5B,mGAAA,OAAO,OAAA;AAChB,+BAQgB;AAPZ,sGAAA,cAAc,OAAA;AACd,oGAAA,YAAY,OAAA;AACZ,iGAAA,SAAS,OAAA;AACT,qHAAA,6BAA6B,OAAA;AAC7B,0GAAA,kBAAkB,OAAA;AAClB,yGAAA,iBAAiB,OAAA;AACjB,mHAAA,2BAA2B,OAAA;AAE/B,+BAAmD;AAA1C,+FAAA,OAAO,OAAA;AAAE,wGAAA,gBAAgB,OAAA","sourcesContent":["/**\n * @webpieces/gcp-identity\n *\n * GCP runtime identity (Node-only). Metadata, Cloud Run URLs, and OIDC mint/verify,\n * all read from the metadata server / ADC at runtime with deterministic localhost\n * fallbacks off-GCP so local dev and tests never touch GCP.\n *\n * Underpins the @AuthOidc service-to-service auth mode (enforced by the framework AuthFilter\n * via an app-bound AuthConfig) and the RPC + Cloud Tasks clients.\n */\n\nexport { isOnGcp } from './metadata';\nexport {\n getServiceName,\n getProjectId,\n getRegion,\n getRuntimeServiceAccountEmail,\n getSelfCloudRunUrl,\n resolveServiceUrl,\n LOCAL_SERVICE_ACCOUNT_EMAIL,\n} from './urls';\nexport { GcpOidc, OidcVerifyResult } from './oidc';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/cloud/gcp-identity/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAEH,uCAA4D;AAAnD,mGAAA,OAAO,OAAA;AAAE,iHAAA,qBAAqB,OAAA;AACvC,+BAOgB;AANZ,sGAAA,cAAc,OAAA;AACd,oGAAA,YAAY,OAAA;AACZ,iGAAA,SAAS,OAAA;AACT,qHAAA,6BAA6B,OAAA;AAC7B,0GAAA,kBAAkB,OAAA;AAClB,mHAAA,2BAA2B,OAAA;AAE/B,2DAA6E;AAApE,wHAAA,kBAAkB,OAAA;AAAE,uHAAA,iBAAiB,OAAA;AAC9C,+BAAmD;AAA1C,+FAAA,OAAO,OAAA;AAAE,wGAAA,gBAAgB,OAAA","sourcesContent":["/**\n * @webpieces/gcp-identity\n *\n * GCP runtime identity (Node-only). Metadata, Cloud Run URLs, and OIDC mint/verify,\n * all read from the metadata server / ADC at runtime with deterministic localhost\n * fallbacks off-GCP so local dev and tests never touch GCP.\n *\n * Underpins the @AuthOidc service-to-service auth mode (enforced by the framework AuthFilter\n * via an app-bound AuthConfig) and the RPC + Cloud Tasks clients.\n *\n * URL RESOLUTION lives in core-util's browser-safe `ClientRegistry`, NOT here; this package\n * contributes only the GCP half of it — {@link gcpCloudRunDeriver}, which an app installs with\n * `ClientRegistry.setDeriver(...)` at startup. That is the seam that lets a non-GCP deployment use\n * `templateDeriver` (or plain mappings) and never pull gcp-metadata onto the URL path.\n */\n\nexport { isOnGcp, resetMetadataForTests } from './metadata';\nexport {\n getServiceName,\n getProjectId,\n getRegion,\n getRuntimeServiceAccountEmail,\n getSelfCloudRunUrl,\n LOCAL_SERVICE_ACCOUNT_EMAIL,\n} from './urls';\nexport { gcpCloudRunDeriver, GcpCloudRunTarget } from './gcpCloudRunDeriver';\nexport { GcpOidc, OidcVerifyResult } from './oidc';\n"]}
package/src/metadata.d.ts CHANGED
@@ -12,3 +12,8 @@ export declare function readNumericProjectId(): Promise<string>;
12
12
  export declare function readRegion(): Promise<string>;
13
13
  /** The service-account email this process runs as. */
14
14
  export declare function readRuntimeServiceAccountEmail(): Promise<string>;
15
+ /**
16
+ * Drop every memoized value. FOR TESTS ONLY — the caches above are module-scope and would otherwise
17
+ * pin the FIRST spec's `K_SERVICE` / metadata answers for the whole file.
18
+ */
19
+ export declare function resetMetadataForTests(): void;
package/src/metadata.js CHANGED
@@ -5,6 +5,7 @@ exports.readProjectId = readProjectId;
5
5
  exports.readNumericProjectId = readNumericProjectId;
6
6
  exports.readRegion = readRegion;
7
7
  exports.readRuntimeServiceAccountEmail = readRuntimeServiceAccountEmail;
8
+ exports.resetMetadataForTests = resetMetadataForTests;
8
9
  const tslib_1 = require("tslib");
9
10
  const gcpMetadata = tslib_1.__importStar(require("gcp-metadata"));
10
11
  /**
@@ -73,4 +74,16 @@ function readRuntimeServiceAccountEmail() {
73
74
  }
74
75
  return cachedSaEmail;
75
76
  }
77
+ /**
78
+ * Drop every memoized value. FOR TESTS ONLY — the caches above are module-scope and would otherwise
79
+ * pin the FIRST spec's `K_SERVICE` / metadata answers for the whole file.
80
+ */
81
+ // webpieces-disable no-function-outside-class -- resets this module's own module-scope memo cells; every sibling here is a free function
82
+ function resetMetadataForTests() {
83
+ cachedOnGcp = undefined;
84
+ cachedProjectId = undefined;
85
+ cachedNumericProjectId = undefined;
86
+ cachedRegion = undefined;
87
+ cachedSaEmail = undefined;
88
+ }
76
89
  //# sourceMappingURL=metadata.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../../../packages/cloud/gcp-identity/src/metadata.ts"],"names":[],"mappings":";;AAyBA,0BAQC;AAUD,sCAKC;AAGD,oDAKC;AAGD,gCAQC;AAGD,wEAKC;;AA3ED,kEAA4C;AAE5C;;;;;;;;;;GAUG;AAEH,IAAI,WAAyC,CAAC;AAC9C,IAAI,eAA4C,CAAC;AACjD,IAAI,sBAAmD,CAAC;AACxD,IAAI,YAAyC,CAAC;AAC9C,IAAI,aAA0C,CAAC;AAE/C;;;;GAIG;AACH,SAAgB,OAAO;IACnB,IAAI,CAAC,WAAW,EAAE,CAAC;QACf,MAAM,KAAK,GACP,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,SAAS;YACtC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,KAAK,gBAAgB,CAAC;QAClE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,WAAW,CAAC;AACvB,CAAC;AAED,oFAAoF;AACpF,wEAAwE;AACxE,iGAAiG;AACjG,SAAS,QAAQ,CAAC,KAAc;IAC5B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,4EAA4E;AAC5E,SAAgB,aAAa;IACzB,IAAI,CAAC,eAAe,EAAE,CAAC;QACnB,eAAe,GAAG,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,eAAe,CAAC;AAC3B,CAAC;AAED,yDAAyD;AACzD,SAAgB,oBAAoB;IAChC,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC1B,sBAAsB,GAAG,WAAW,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,sBAAsB,CAAC;AAClC,CAAC;AAED,4FAA4F;AAC5F,SAAgB,UAAU;IACtB,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,GAAW,EAAE,EAAE;YAC9E,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACjC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACnD,CAAC,CAAC,CAAC;IACP,CAAC;IACD,OAAO,YAAY,CAAC;AACxB,CAAC;AAED,sDAAsD;AACtD,SAAgB,8BAA8B;IAC1C,IAAI,CAAC,aAAa,EAAE,CAAC;QACjB,aAAa,GAAG,WAAW,CAAC,QAAQ,CAAC,gCAAgC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,aAAa,CAAC;AACzB,CAAC","sourcesContent":["import * as gcpMetadata from 'gcp-metadata';\n\n/**\n * Cached reads of the GCP metadata server. Every value is fetched at most once per\n * process (the metadata server is stable for the life of the instance). Off-GCP\n * `isOnGcp()` is false and the callers fall back to localhost values, so nothing\n * here is ever reached in local dev / tests.\n *\n * `isOnGcp()` is decided from the `K_SERVICE` env var (which Cloud Run always sets),\n * NOT a metadata network probe — so local dev never blocks on the ~3s\n * `gcpMetadata.isAvailable()` timeout. Every caller (project id, region, Cloud Run URL\n * resolution, OIDC minting, Cloud Tasks) short-circuits through this one gate.\n */\n\nlet cachedOnGcp: Promise<boolean> | undefined;\nlet cachedProjectId: Promise<string> | undefined;\nlet cachedNumericProjectId: Promise<string> | undefined;\nlet cachedRegion: Promise<string> | undefined;\nlet cachedSaEmail: Promise<string> | undefined;\n\n/**\n * True when running on GCP Cloud Run. Gated on `K_SERVICE` (set by Cloud Run) so local dev does NO\n * metadata network probe. For non-Cloud-Run GCP (GCE / Cloud Functions, which don't set `K_SERVICE`),\n * set `METADATA_SERVER_DETECTION=assume-present` to force on-GCP behavior.\n */\nexport function isOnGcp(): Promise<boolean> {\n if (!cachedOnGcp) {\n const onGcp =\n process.env['K_SERVICE'] !== undefined ||\n process.env['METADATA_SERVER_DETECTION'] === 'assume-present';\n cachedOnGcp = Promise.resolve(onGcp);\n }\n return cachedOnGcp;\n}\n\n// gcp-metadata returns loosely-typed values; coerce every metadata read to a string\n// through this one helper so the rest of the file stays strongly typed.\n// webpieces-disable no-any-unknown -- single coercion point for gcp-metadata's loose return type\nfunction asString(value: unknown): string {\n return String(value);\n}\n\n/** GCP project id (e.g. 'my-project'). Only call when isOnGcp() is true. */\nexport function readProjectId(): Promise<string> {\n if (!cachedProjectId) {\n cachedProjectId = gcpMetadata.project('project-id').then(asString);\n }\n return cachedProjectId;\n}\n\n/** Numeric project id (used to build Cloud Run URLs). */\nexport function readNumericProjectId(): Promise<string> {\n if (!cachedNumericProjectId) {\n cachedNumericProjectId = gcpMetadata.project('numeric-project-id').then(asString);\n }\n return cachedNumericProjectId;\n}\n\n/** Cloud Run region (e.g. 'us-central1'), parsed from 'projects/<num>/regions/<region>'. */\nexport function readRegion(): Promise<string> {\n if (!cachedRegion) {\n cachedRegion = gcpMetadata.instance('region').then(asString).then((raw: string) => {\n const idx = raw.lastIndexOf('/');\n return idx >= 0 ? raw.substring(idx + 1) : raw;\n });\n }\n return cachedRegion;\n}\n\n/** The service-account email this process runs as. */\nexport function readRuntimeServiceAccountEmail(): Promise<string> {\n if (!cachedSaEmail) {\n cachedSaEmail = gcpMetadata.instance('service-accounts/default/email').then(asString);\n }\n return cachedSaEmail;\n}\n"]}
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../../../packages/cloud/gcp-identity/src/metadata.ts"],"names":[],"mappings":";;AAyBA,0BAQC;AAUD,sCAKC;AAGD,oDAKC;AAGD,gCAQC;AAGD,wEAKC;AAOD,sDAMC;;AAxFD,kEAA4C;AAE5C;;;;;;;;;;GAUG;AAEH,IAAI,WAAyC,CAAC;AAC9C,IAAI,eAA4C,CAAC;AACjD,IAAI,sBAAmD,CAAC;AACxD,IAAI,YAAyC,CAAC;AAC9C,IAAI,aAA0C,CAAC;AAE/C;;;;GAIG;AACH,SAAgB,OAAO;IACnB,IAAI,CAAC,WAAW,EAAE,CAAC;QACf,MAAM,KAAK,GACP,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,SAAS;YACtC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,KAAK,gBAAgB,CAAC;QAClE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,WAAW,CAAC;AACvB,CAAC;AAED,oFAAoF;AACpF,wEAAwE;AACxE,iGAAiG;AACjG,SAAS,QAAQ,CAAC,KAAc;IAC5B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,4EAA4E;AAC5E,SAAgB,aAAa;IACzB,IAAI,CAAC,eAAe,EAAE,CAAC;QACnB,eAAe,GAAG,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,eAAe,CAAC;AAC3B,CAAC;AAED,yDAAyD;AACzD,SAAgB,oBAAoB;IAChC,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC1B,sBAAsB,GAAG,WAAW,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,sBAAsB,CAAC;AAClC,CAAC;AAED,4FAA4F;AAC5F,SAAgB,UAAU;IACtB,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,GAAW,EAAE,EAAE;YAC9E,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACjC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACnD,CAAC,CAAC,CAAC;IACP,CAAC;IACD,OAAO,YAAY,CAAC;AACxB,CAAC;AAED,sDAAsD;AACtD,SAAgB,8BAA8B;IAC1C,IAAI,CAAC,aAAa,EAAE,CAAC;QACjB,aAAa,GAAG,WAAW,CAAC,QAAQ,CAAC,gCAAgC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,aAAa,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,yIAAyI;AACzI,SAAgB,qBAAqB;IACjC,WAAW,GAAG,SAAS,CAAC;IACxB,eAAe,GAAG,SAAS,CAAC;IAC5B,sBAAsB,GAAG,SAAS,CAAC;IACnC,YAAY,GAAG,SAAS,CAAC;IACzB,aAAa,GAAG,SAAS,CAAC;AAC9B,CAAC","sourcesContent":["import * as gcpMetadata from 'gcp-metadata';\n\n/**\n * Cached reads of the GCP metadata server. Every value is fetched at most once per\n * process (the metadata server is stable for the life of the instance). Off-GCP\n * `isOnGcp()` is false and the callers fall back to localhost values, so nothing\n * here is ever reached in local dev / tests.\n *\n * `isOnGcp()` is decided from the `K_SERVICE` env var (which Cloud Run always sets),\n * NOT a metadata network probe — so local dev never blocks on the ~3s\n * `gcpMetadata.isAvailable()` timeout. Every caller (project id, region, Cloud Run URL\n * resolution, OIDC minting, Cloud Tasks) short-circuits through this one gate.\n */\n\nlet cachedOnGcp: Promise<boolean> | undefined;\nlet cachedProjectId: Promise<string> | undefined;\nlet cachedNumericProjectId: Promise<string> | undefined;\nlet cachedRegion: Promise<string> | undefined;\nlet cachedSaEmail: Promise<string> | undefined;\n\n/**\n * True when running on GCP Cloud Run. Gated on `K_SERVICE` (set by Cloud Run) so local dev does NO\n * metadata network probe. For non-Cloud-Run GCP (GCE / Cloud Functions, which don't set `K_SERVICE`),\n * set `METADATA_SERVER_DETECTION=assume-present` to force on-GCP behavior.\n */\nexport function isOnGcp(): Promise<boolean> {\n if (!cachedOnGcp) {\n const onGcp =\n process.env['K_SERVICE'] !== undefined ||\n process.env['METADATA_SERVER_DETECTION'] === 'assume-present';\n cachedOnGcp = Promise.resolve(onGcp);\n }\n return cachedOnGcp;\n}\n\n// gcp-metadata returns loosely-typed values; coerce every metadata read to a string\n// through this one helper so the rest of the file stays strongly typed.\n// webpieces-disable no-any-unknown -- single coercion point for gcp-metadata's loose return type\nfunction asString(value: unknown): string {\n return String(value);\n}\n\n/** GCP project id (e.g. 'my-project'). Only call when isOnGcp() is true. */\nexport function readProjectId(): Promise<string> {\n if (!cachedProjectId) {\n cachedProjectId = gcpMetadata.project('project-id').then(asString);\n }\n return cachedProjectId;\n}\n\n/** Numeric project id (used to build Cloud Run URLs). */\nexport function readNumericProjectId(): Promise<string> {\n if (!cachedNumericProjectId) {\n cachedNumericProjectId = gcpMetadata.project('numeric-project-id').then(asString);\n }\n return cachedNumericProjectId;\n}\n\n/** Cloud Run region (e.g. 'us-central1'), parsed from 'projects/<num>/regions/<region>'. */\nexport function readRegion(): Promise<string> {\n if (!cachedRegion) {\n cachedRegion = gcpMetadata.instance('region').then(asString).then((raw: string) => {\n const idx = raw.lastIndexOf('/');\n return idx >= 0 ? raw.substring(idx + 1) : raw;\n });\n }\n return cachedRegion;\n}\n\n/** The service-account email this process runs as. */\nexport function readRuntimeServiceAccountEmail(): Promise<string> {\n if (!cachedSaEmail) {\n cachedSaEmail = gcpMetadata.instance('service-accounts/default/email').then(asString);\n }\n return cachedSaEmail;\n}\n\n/**\n * Drop every memoized value. FOR TESTS ONLY — the caches above are module-scope and would otherwise\n * pin the FIRST spec's `K_SERVICE` / metadata answers for the whole file.\n */\n// webpieces-disable no-function-outside-class -- resets this module's own module-scope memo cells; every sibling here is a free function\nexport function resetMetadataForTests(): void {\n cachedOnGcp = undefined;\n cachedProjectId = undefined;\n cachedNumericProjectId = undefined;\n cachedRegion = undefined;\n cachedSaEmail = undefined;\n}\n"]}
package/src/urls.d.ts CHANGED
@@ -1,8 +1,13 @@
1
1
  /** Local fallback SA email used off-GCP so 'self' caller checks are deterministic. */
2
2
  export declare const LOCAL_SERVICE_ACCOUNT_EMAIL = "local@localhost.invalid";
3
3
  /**
4
- * Logical service name from K_SERVICE (Cloud Run sets this), stripping a leading
5
- * 'tf-' Terraform prefix. Off-GCP returns 'local'. Synchronous — env only.
4
+ * This service's name from K_SERVICE (Cloud Run sets this), VERBATIM. Off-GCP returns 'local'.
5
+ * Synchronous — env only.
6
+ *
7
+ * There is exactly ONE service name and no prefix rules: the Cloud Run service name is the svcName
8
+ * peers call you by, is what {@link getSelfCloudRunUrl} puts in your own URL, and is what
9
+ * `gcpCloudRunDeriver` puts in theirs. (This used to strip a leading `tf-`, which made a service
10
+ * deployed as `tf-server2` unreachable by the name it reported — the two never agreed.)
6
11
  */
7
12
  export declare function getServiceName(): string;
8
13
  /** GCP project id, or 'local-project' off-GCP. */
@@ -16,19 +21,3 @@ export declare function getRuntimeServiceAccountEmail(): Promise<string>;
16
21
  * Off-GCP → http://localhost:<PORT>.
17
22
  */
18
23
  export declare function getSelfCloudRunUrl(): Promise<string>;
19
- /**
20
- * Resolve the base URL a client should call for `svcName`. The ONE resolver every RPC / Cloud Tasks
21
- * client uses. Precedence:
22
- *
23
- * 1. A {@link ClientRegistry} override wins — this is how you reach anything the derivation cannot
24
- * describe: a localhost port, another region, another project, a non-Cloud-Run host. Each
25
- * environment populates the registry from its own per-env config.
26
- * 2. Otherwise, on GCP the URL is DERIVED from the Cloud Run service name + project + region
27
- * (via {@link isOnGcp}) — same project, same region, zero maintenance across demo/qa/prod.
28
- * 3. Otherwise (off-GCP and unregistered) it THROWS — a missing mapping is a setup bug, not a
29
- * silent mis-route.
30
- *
31
- * So clients carry ONLY a `svcName`; there is no per-client `targetUrl` (a client is built once but a
32
- * URL is per-environment — that belongs in the registry, not on the client).
33
- */
34
- export declare function resolveServiceUrl(svcName: string): Promise<string>;
package/src/urls.js CHANGED
@@ -6,21 +6,20 @@ exports.getProjectId = getProjectId;
6
6
  exports.getRegion = getRegion;
7
7
  exports.getRuntimeServiceAccountEmail = getRuntimeServiceAccountEmail;
8
8
  exports.getSelfCloudRunUrl = getSelfCloudRunUrl;
9
- exports.resolveServiceUrl = resolveServiceUrl;
10
- const core_util_1 = require("@webpieces/core-util");
11
9
  const metadata_1 = require("./metadata");
12
10
  /** Local fallback SA email used off-GCP so 'self' caller checks are deterministic. */
13
11
  exports.LOCAL_SERVICE_ACCOUNT_EMAIL = 'local@localhost.invalid';
14
12
  /**
15
- * Logical service name from K_SERVICE (Cloud Run sets this), stripping a leading
16
- * 'tf-' Terraform prefix. Off-GCP returns 'local'. Synchronous — env only.
13
+ * This service's name from K_SERVICE (Cloud Run sets this), VERBATIM. Off-GCP returns 'local'.
14
+ * Synchronous — env only.
15
+ *
16
+ * There is exactly ONE service name and no prefix rules: the Cloud Run service name is the svcName
17
+ * peers call you by, is what {@link getSelfCloudRunUrl} puts in your own URL, and is what
18
+ * `gcpCloudRunDeriver` puts in theirs. (This used to strip a leading `tf-`, which made a service
19
+ * deployed as `tf-server2` unreachable by the name it reported — the two never agreed.)
17
20
  */
18
21
  function getServiceName() {
19
- const kService = process.env['K_SERVICE'];
20
- if (!kService) {
21
- return 'local';
22
- }
23
- return kService.startsWith('tf-') ? kService.substring('tf-'.length) : kService;
22
+ return process.env['K_SERVICE'] ?? 'local';
24
23
  }
25
24
  /** GCP project id, or 'local-project' off-GCP. */
26
25
  async function getProjectId() {
@@ -57,33 +56,4 @@ async function getSelfCloudRunUrl() {
57
56
  const region = await (0, metadata_1.readRegion)();
58
57
  return `https://${kService}-${numericProjectId}.${region}.run.app`;
59
58
  }
60
- /**
61
- * Resolve the base URL a client should call for `svcName`. The ONE resolver every RPC / Cloud Tasks
62
- * client uses. Precedence:
63
- *
64
- * 1. A {@link ClientRegistry} override wins — this is how you reach anything the derivation cannot
65
- * describe: a localhost port, another region, another project, a non-Cloud-Run host. Each
66
- * environment populates the registry from its own per-env config.
67
- * 2. Otherwise, on GCP the URL is DERIVED from the Cloud Run service name + project + region
68
- * (via {@link isOnGcp}) — same project, same region, zero maintenance across demo/qa/prod.
69
- * 3. Otherwise (off-GCP and unregistered) it THROWS — a missing mapping is a setup bug, not a
70
- * silent mis-route.
71
- *
72
- * So clients carry ONLY a `svcName`; there is no per-client `targetUrl` (a client is built once but a
73
- * URL is per-environment — that belongs in the registry, not on the client).
74
- */
75
- // webpieces-disable no-function-outside-class -- pure GCP url helper; every sibling in this module is a free function
76
- async function resolveServiceUrl(svcName) {
77
- const override = core_util_1.ClientRegistry.tryLookup(svcName);
78
- if (override) {
79
- return override;
80
- }
81
- if (!(await (0, metadata_1.isOnGcp)())) {
82
- throw new Error(`No URL for service "${svcName}". On GCP it is derived from the Cloud Run service name; ` +
83
- `off-GCP register it: ClientRegistry.addMapping(svcName, port) or addUrlMapping(svcName, url).`);
84
- }
85
- const numericProjectId = await (0, metadata_1.readNumericProjectId)();
86
- const region = await (0, metadata_1.readRegion)();
87
- return `https://${svcName}-${numericProjectId}.${region}.run.app`;
88
- }
89
59
  //# sourceMappingURL=urls.js.map
package/src/urls.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../../../../packages/cloud/gcp-identity/src/urls.ts"],"names":[],"mappings":";;;AAUA,wCAMC;AAGD,oCAKC;AAGD,8BAKC;AAGD,sEAKC;AAMD,gDASC;AAkBD,8CAcC;AAvFD,oDAAsD;AACtD,yCAAsH;AAEtH,sFAAsF;AACzE,QAAA,2BAA2B,GAAG,yBAAyB,CAAC;AAErE;;;GAGG;AACH,SAAgB,cAAc;IAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACnB,CAAC;IACD,OAAO,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACpF,CAAC;AAED,kDAAkD;AAC3C,KAAK,UAAU,YAAY;IAC9B,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,EAAE,CAAC;QACrB,OAAO,eAAe,CAAC;IAC3B,CAAC;IACD,OAAO,IAAA,wBAAa,GAAE,CAAC;AAC3B,CAAC;AAED,4CAA4C;AACrC,KAAK,UAAU,SAAS;IAC3B,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC;IACnB,CAAC;IACD,OAAO,IAAA,qBAAU,GAAE,CAAC;AACxB,CAAC;AAED,8DAA8D;AACvD,KAAK,UAAU,6BAA6B;IAC/C,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,EAAE,CAAC;QACrB,OAAO,mCAA2B,CAAC;IACvC,CAAC;IACD,OAAO,IAAA,yCAA8B,GAAE,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,kBAAkB;IACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;QAC3C,OAAO,oBAAoB,IAAI,EAAE,CAAC;IACtC,CAAC;IACD,MAAM,gBAAgB,GAAG,MAAM,IAAA,+BAAoB,GAAE,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAU,GAAE,CAAC;IAClC,OAAO,WAAW,QAAQ,IAAI,gBAAgB,IAAI,MAAM,UAAU,CAAC;AACvE,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,sHAAsH;AAC/G,KAAK,UAAU,iBAAiB,CAAC,OAAe;IACnD,MAAM,QAAQ,GAAG,0BAAc,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACnD,IAAI,QAAQ,EAAE,CAAC;QACX,OAAO,QAAQ,CAAC;IACpB,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACX,uBAAuB,OAAO,2DAA2D;YACzF,+FAA+F,CAClG,CAAC;IACN,CAAC;IACD,MAAM,gBAAgB,GAAG,MAAM,IAAA,+BAAoB,GAAE,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAU,GAAE,CAAC;IAClC,OAAO,WAAW,OAAO,IAAI,gBAAgB,IAAI,MAAM,UAAU,CAAC;AACtE,CAAC","sourcesContent":["import { ClientRegistry } from '@webpieces/core-util';\nimport { isOnGcp, readNumericProjectId, readRegion, readProjectId, readRuntimeServiceAccountEmail } from './metadata';\n\n/** Local fallback SA email used off-GCP so 'self' caller checks are deterministic. */\nexport const LOCAL_SERVICE_ACCOUNT_EMAIL = 'local@localhost.invalid';\n\n/**\n * Logical service name from K_SERVICE (Cloud Run sets this), stripping a leading\n * 'tf-' Terraform prefix. Off-GCP returns 'local'. Synchronousenv only.\n */\nexport function getServiceName(): string {\n const kService = process.env['K_SERVICE'];\n if (!kService) {\n return 'local';\n }\n return kService.startsWith('tf-') ? kService.substring('tf-'.length) : kService;\n}\n\n/** GCP project id, or 'local-project' off-GCP. */\nexport async function getProjectId(): Promise<string> {\n if (!(await isOnGcp())) {\n return 'local-project';\n }\n return readProjectId();\n}\n\n/** Cloud Run region, or 'local' off-GCP. */\nexport async function getRegion(): Promise<string> {\n if (!(await isOnGcp())) {\n return 'local';\n }\n return readRegion();\n}\n\n/** The runtime SA email, or the local placeholder off-GCP. */\nexport async function getRuntimeServiceAccountEmail(): Promise<string> {\n if (!(await isOnGcp())) {\n return LOCAL_SERVICE_ACCOUNT_EMAIL;\n }\n return readRuntimeServiceAccountEmail();\n}\n\n/**\n * This service's own base URL (its Cloud Tasks self-enqueue target / public base).\n * Off-GCP → http://localhost:<PORT>.\n */\nexport async function getSelfCloudRunUrl(): Promise<string> {\n const kService = process.env['K_SERVICE'];\n if (!(await isOnGcp()) || !kService) {\n const port = process.env['PORT'] ?? '8080';\n return `http://localhost:${port}`;\n }\n const numericProjectId = await readNumericProjectId();\n const region = await readRegion();\n return `https://${kService}-${numericProjectId}.${region}.run.app`;\n}\n\n/**\n * Resolve the base URL a client should call for `svcName`. The ONE resolver every RPC / Cloud Tasks\n * client uses. Precedence:\n *\n * 1. A {@link ClientRegistry} override wins — this is how you reach anything the derivation cannot\n * describe: a localhost port, another region, another project, a non-Cloud-Run host. Each\n * environment populates the registry from its own per-env config.\n * 2. Otherwise, on GCP the URL is DERIVED from the Cloud Run service name + project + region\n * (via {@link isOnGcp}) — same project, same region, zero maintenance across demo/qa/prod.\n * 3. Otherwise (off-GCP and unregistered) it THROWS — a missing mapping is a setup bug, not a\n * silent mis-route.\n *\n * So clients carry ONLY a `svcName`; there is no per-client `targetUrl` (a client is built once but a\n * URL is per-environment — that belongs in the registry, not on the client).\n */\n// webpieces-disable no-function-outside-class -- pure GCP url helper; every sibling in this module is a free function\nexport async function resolveServiceUrl(svcName: string): Promise<string> {\n const override = ClientRegistry.tryLookup(svcName);\n if (override) {\n return override;\n }\n if (!(await isOnGcp())) {\n throw new Error(\n `No URL for service \"${svcName}\". On GCP it is derived from the Cloud Run service name; ` +\n `off-GCP register it: ClientRegistry.addMapping(svcName, port) or addUrlMapping(svcName, url).`,\n );\n }\n const numericProjectId = await readNumericProjectId();\n const region = await readRegion();\n return `https://${svcName}-${numericProjectId}.${region}.run.app`;\n}\n"]}
1
+ {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../../../../packages/cloud/gcp-identity/src/urls.ts"],"names":[],"mappings":";;;AAcA,wCAEC;AAGD,oCAKC;AAGD,8BAKC;AAGD,sEAKC;AAMD,gDASC;AAvDD,yCAAsH;AAEtH,sFAAsF;AACzE,QAAA,2BAA2B,GAAG,yBAAyB,CAAC;AAErE;;;;;;;;GAQG;AACH,SAAgB,cAAc;IAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC;AAC/C,CAAC;AAED,kDAAkD;AAC3C,KAAK,UAAU,YAAY;IAC9B,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,EAAE,CAAC;QACrB,OAAO,eAAe,CAAC;IAC3B,CAAC;IACD,OAAO,IAAA,wBAAa,GAAE,CAAC;AAC3B,CAAC;AAED,4CAA4C;AACrC,KAAK,UAAU,SAAS;IAC3B,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC;IACnB,CAAC;IACD,OAAO,IAAA,qBAAU,GAAE,CAAC;AACxB,CAAC;AAED,8DAA8D;AACvD,KAAK,UAAU,6BAA6B;IAC/C,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,EAAE,CAAC;QACrB,OAAO,mCAA2B,CAAC;IACvC,CAAC;IACD,OAAO,IAAA,yCAA8B,GAAE,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,kBAAkB;IACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,CAAC,CAAC,MAAM,IAAA,kBAAO,GAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;QAC3C,OAAO,oBAAoB,IAAI,EAAE,CAAC;IACtC,CAAC;IACD,MAAM,gBAAgB,GAAG,MAAM,IAAA,+BAAoB,GAAE,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAU,GAAE,CAAC;IAClC,OAAO,WAAW,QAAQ,IAAI,gBAAgB,IAAI,MAAM,UAAU,CAAC;AACvE,CAAC","sourcesContent":["import { isOnGcp, readNumericProjectId, readRegion, readProjectId, readRuntimeServiceAccountEmail } from './metadata';\n\n/** Local fallback SA email used off-GCP so 'self' caller checks are deterministic. */\nexport const LOCAL_SERVICE_ACCOUNT_EMAIL = 'local@localhost.invalid';\n\n/**\n * This service's name from K_SERVICE (Cloud Run sets this), VERBATIM. Off-GCP returns 'local'.\n * Synchronous — env only.\n *\n * There is exactly ONE service name and no prefix rules: the Cloud Run service name is the svcName\n * peers call you by, is what {@link getSelfCloudRunUrl} puts in your own URL, and is what\n * `gcpCloudRunDeriver` puts in theirs. (This used to strip a leading `tf-`, which made a service\n * deployed as `tf-server2` unreachable by the name it reportedthe two never agreed.)\n */\nexport function getServiceName(): string {\n return process.env['K_SERVICE'] ?? 'local';\n}\n\n/** GCP project id, or 'local-project' off-GCP. */\nexport async function getProjectId(): Promise<string> {\n if (!(await isOnGcp())) {\n return 'local-project';\n }\n return readProjectId();\n}\n\n/** Cloud Run region, or 'local' off-GCP. */\nexport async function getRegion(): Promise<string> {\n if (!(await isOnGcp())) {\n return 'local';\n }\n return readRegion();\n}\n\n/** The runtime SA email, or the local placeholder off-GCP. */\nexport async function getRuntimeServiceAccountEmail(): Promise<string> {\n if (!(await isOnGcp())) {\n return LOCAL_SERVICE_ACCOUNT_EMAIL;\n }\n return readRuntimeServiceAccountEmail();\n}\n\n/**\n * This service's own base URL (its Cloud Tasks self-enqueue target / public base).\n * Off-GCP → http://localhost:<PORT>.\n */\nexport async function getSelfCloudRunUrl(): Promise<string> {\n const kService = process.env['K_SERVICE'];\n if (!(await isOnGcp()) || !kService) {\n const port = process.env['PORT'] ?? '8080';\n return `http://localhost:${port}`;\n }\n const numericProjectId = await readNumericProjectId();\n const region = await readRegion();\n return `https://${kService}-${numericProjectId}.${region}.run.app`;\n}\n"]}