@postman-cse/onboarding-gcp-spec-discovery 1.0.0
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/LICENSE +21 -0
- package/README.md +181 -0
- package/RELEASE_POLICY.md +43 -0
- package/SECURITY.md +32 -0
- package/SUPPORT.md +30 -0
- package/action.yml +98 -0
- package/dist/cli.cjs +48807 -0
- package/dist/index.cjs +51275 -0
- package/docs/LIVE_TESTING_RUNBOOK.md +41 -0
- package/docs/PRD.md +223 -0
- package/docs/providers.md +117 -0
- package/docs/repository-association.md +70 -0
- package/package.json +70 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Live GCP validation runbook
|
|
2
|
+
|
|
3
|
+
Live validation provisions disposable GCP resources, runs the compiled CLI, captures sanitized evidence, and tears down only resources created by the current run. It is operator-triggered and is not part of pull-request CI.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Access to project `dans-project-491920` with permission to create and delete the run-scoped validation resources.
|
|
8
|
+
- ADC credentials from `gcloud auth application-default login`, or equivalent workload identity credentials.
|
|
9
|
+
- A fresh bundle: `npm run build`.
|
|
10
|
+
|
|
11
|
+
Set the project explicitly:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
export GCP_PROJECT_ID=dans-project-491920
|
|
15
|
+
node validation/scripts/validate-live-gcp-surfaces.mjs --provision --teardown
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Validation cases
|
|
19
|
+
|
|
20
|
+
The runner provisions run-marked API Gateway, Cloud Endpoints, and Apigee proxy resources (including a `postman-repo`-labeled config plus two configs sharing a conflict label), then executes these ten cases:
|
|
21
|
+
|
|
22
|
+
1. `gateway-explicit-api-id`
|
|
23
|
+
2. `gateway-discovery`
|
|
24
|
+
3. `gateway-repo-label` — resolves from the `postman-repo` label and repository identity alone
|
|
25
|
+
4. `gateway-label-conflict` — two configs sharing one `postman-repo` value must stay unresolved
|
|
26
|
+
5. `endpoints-explicit-api-id`
|
|
27
|
+
6. `endpoints-discovery`
|
|
28
|
+
7. `apigee-discovery`
|
|
29
|
+
8. `discover-many`
|
|
30
|
+
9. `iac-single`
|
|
31
|
+
10. `ambiguity`
|
|
32
|
+
|
|
33
|
+
It writes sanitized evidence only: case name, status, source type, provider type, and specification format. Do not commit resource IDs, hostnames, URLs, credentials, manifests, proxy archives, or specification bodies.
|
|
34
|
+
|
|
35
|
+
## Safety rules
|
|
36
|
+
|
|
37
|
+
- `--provision` is required before the script creates resources.
|
|
38
|
+
- `--teardown` removes only resources carrying the exact current-run marker.
|
|
39
|
+
- The runner never deletes shared Apigee organization, environment, environment group, or instance resources.
|
|
40
|
+
- Do not use a production project or broaden cleanup beyond the current run.
|
|
41
|
+
- Use ADC or workload identity; do not record credential material in evidence.
|
package/docs/PRD.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# GCP Spec Discovery Action v1.0.0 PRD
|
|
2
|
+
|
|
3
|
+
Status: implementation-ready
|
|
4
|
+
Authoritative plan: `.plans/gcp-spec-discovery-v1-plan.md`
|
|
5
|
+
Repository: `postman-cs/postman-gcp-spec-discovery-action`
|
|
6
|
+
Local path: `cse/postman-gcp-spec-discovery-action/`
|
|
7
|
+
Release: `v1.0.0`; npm `@postman-cse/onboarding-gcp-spec-discovery@1.0.0`
|
|
8
|
+
|
|
9
|
+
## Product Contract
|
|
10
|
+
|
|
11
|
+
The action discovers or derives an OpenAPI 3.x or Swagger 2.0 document from one explicitly named Google Cloud project. Resolution order is explicit `api-id` override, then direct repo spec, one local-IaC referenced spec, then cloud candidates. Ambiguity or unsupported source shape returns `unresolved/manual-review` with sanitized evidence and writes no guessed artifact.
|
|
12
|
+
|
|
13
|
+
### Inputs
|
|
14
|
+
|
|
15
|
+
`action.yml` and `src/contracts.ts` MUST declare these keys in order:
|
|
16
|
+
|
|
17
|
+
| Input | Required | Default | Meaning |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| `mode` | false | `resolve-one` | `resolve-one` or `discover-many`. |
|
|
20
|
+
| `project-id` | true | none | Exact Google Cloud project ID; never inferred from ambient ADC. |
|
|
21
|
+
| `location` | false | `global` | API Gateway location; v1 accepts only `global`. |
|
|
22
|
+
| `api-id` | false | `''` | Exact full resource name of any explicitly selectable source: API Gateway config, Cloud Endpoints config, Apigee proxy revision, API Hub spec, Apigee portal apidoc, Vertex extension, Dialogflow tool, or CES tool/toolset. |
|
|
23
|
+
| `repo-slug` | false | `''` | Repository slug (owner/name) for repository-association matching against `postman-repo` labels; defaults to the CI-detected repository. |
|
|
24
|
+
| `expected-service-name` | false | `''` | Ranking hint; also names manual-review results. |
|
|
25
|
+
| `expected-api-ids-json` | false | `[]` | JSON array of expected full resource names; exact matches rank highest. |
|
|
26
|
+
| `service-mapping-json` | false | `{}` | JSON object mapping resource names to service names. |
|
|
27
|
+
| `output-dir` | false | `discovered-specs` | Confined repository-relative write root. |
|
|
28
|
+
| `postman-api-key` | false | `''` | Optional telemetry enrichment only. |
|
|
29
|
+
| `postman-access-token` | false | `''` | Optional telemetry enrichment only. |
|
|
30
|
+
|
|
31
|
+
CLI-only controls mirror Azure: repository context, filter regex, candidate cap, dry-run, preflight controls, request timeout, attempts, result JSON, dotenv path, help, and version. Defaults are `max-candidates=50`, `request-timeout-ms=30000`, `max-attempts=3`, `preflight-checks=true`, and `preflight-permission-probe=true`.
|
|
32
|
+
|
|
33
|
+
### Outputs
|
|
34
|
+
|
|
35
|
+
The action MUST set all 22 outputs in this order on every successful invocation:
|
|
36
|
+
|
|
37
|
+
1. `resolution-json`
|
|
38
|
+
2. `resolution-status`
|
|
39
|
+
3. `source-type`
|
|
40
|
+
4. `mapping-confidence`
|
|
41
|
+
5. `spec-path`
|
|
42
|
+
6. `api-id`
|
|
43
|
+
7. `service-name`
|
|
44
|
+
8. `services-json`
|
|
45
|
+
9. `service-count`
|
|
46
|
+
10. `export-summary-json`
|
|
47
|
+
11. `candidates-json`
|
|
48
|
+
12. `provider-type`
|
|
49
|
+
13. `spec-format`
|
|
50
|
+
14. `contract-origin`
|
|
51
|
+
15. `contract-metadata-path`
|
|
52
|
+
16. `variant-count`
|
|
53
|
+
17. `derived-openapi-path`
|
|
54
|
+
18. `derived-openapi-version`
|
|
55
|
+
19. `derived-openapi-completeness`
|
|
56
|
+
20. `derived-openapi-format`
|
|
57
|
+
21. `derived-openapi-evidence-json`
|
|
58
|
+
22. `narrowing-strategy`
|
|
59
|
+
|
|
60
|
+
Provider types are `api-gateway | cloud-endpoints | apigee | api-hub | app-integration | connectors-custom | apigee-portal | vertex-extensions | agent-engines | dialogflow-tools | ces-toolsets | iac-local`. Source types are `repo-spec | api-gateway-config | cloud-endpoints-config | apigee-proxy | apigee-env-oas | api-hub-spec | app-integration-trigger | connectors-custom-spec | connectors-generated-spec | apigee-portal-doc | vertex-extension-manifest | agent-engine-generated-spec | dialogflow-tool-schema | ces-tool-schema | ces-toolset-schema | iac-embedded | manual-review | discover-many`. Spec formats are `openapi-json | openapi-yaml`. Compatibility outputs 14-16 are empty in v1. Dotenv names use `POSTMAN_GCP_SPEC_*`.
|
|
61
|
+
|
|
62
|
+
## Requirements
|
|
63
|
+
|
|
64
|
+
### R1 - Scaffold, contract, input resolution, and confinement (P0)
|
|
65
|
+
|
|
66
|
+
- Create an independent Node 24 strict-TypeScript package with ESM source and single-file CommonJS `dist/index.cjs` plus executable `dist/cli.cjs`.
|
|
67
|
+
- Require `project-id`; validate it against `^[a-z][a-z0-9-]{4,28}[a-z0-9]$`. Reject any `location` other than `global` in v1.
|
|
68
|
+
- Confine output/result/dotenv paths beneath `repo-root` lexically and by realpath; reject symlink/path traversal before writing.
|
|
69
|
+
- Keep Action/CLI adapters thin and byte-equivalent at the output surface.
|
|
70
|
+
|
|
71
|
+
Acceptance:
|
|
72
|
+
|
|
73
|
+
- **GCP-CONTRACT-001:** manifest and contract inputs/outputs exactly match the ordered lists.
|
|
74
|
+
- **GCP-CONTRACT-002:** defaults and invalid mode/project/location produce exact actionable errors.
|
|
75
|
+
- **GCP-CONTRACT-003:** `../escape` and out-of-root symlink outputs fail with no outside write through runtime and CLI.
|
|
76
|
+
- **GCP-CONTRACT-004:** resolved/unresolved and both modes serialize all 22 outputs with documented empty values.
|
|
77
|
+
- **GCP-CONTRACT-005:** package name/version/Node engine/files and exact dependency pins match the release contract.
|
|
78
|
+
|
|
79
|
+
### R2 - ADC transport, preflight, retry, pagination, and sanitization (P0)
|
|
80
|
+
|
|
81
|
+
- Construct exactly one `GoogleAuth({ scopes: ['https://www.googleapis.com/auth/cloud-platform'] })` in production and reuse its authenticated fetch transport.
|
|
82
|
+
- Preflight calls Cloud Resource Manager `projects.get` for the explicit project before provider enumeration. Credential/project failure is fatal; provider 401/403/service-disabled failures are fail-soft typed probes.
|
|
83
|
+
- Every request uses `request-timeout-ms`, at most `max-attempts`, retries only 408/429/5xx, and never retries validation/400/401/403/404 responses.
|
|
84
|
+
- Consume `nextPageToken` until empty; reject a repeated token. URL path segments use `encodeURIComponent`; query strings are constructed with `URLSearchParams`.
|
|
85
|
+
- Sanitize access tokens, credential JSON, project numbers, service-account emails, full resource names, and URL queries from errors/evidence.
|
|
86
|
+
|
|
87
|
+
Acceptance:
|
|
88
|
+
|
|
89
|
+
- **GCP-CLIENT-001:** one shared auth object, successful preflight, and fatal preflight failure before providers.
|
|
90
|
+
- **GCP-CLIENT-002:** retry attempt bounds and nonretryable behavior are exact under fake transport.
|
|
91
|
+
- **GCP-CLIENT-003:** two-page lists combine in order; repeated token fails after the second page.
|
|
92
|
+
- **GCP-CLIENT-004:** mixed provider 403/error/success yields ordered `skipped:iam`, `skipped:error`, `available` and continues.
|
|
93
|
+
- **GCP-CLIENT-005:** raw secret/project-number/email/resource/query fixtures are absent after sanitization.
|
|
94
|
+
|
|
95
|
+
### R3 - API Gateway original-source provider (P0)
|
|
96
|
+
|
|
97
|
+
- List `projects/{project}/locations/global/apis` and each API's configs with pagination.
|
|
98
|
+
- Retain configs in `ACTIVE`; keep unsupported gRPC-only/non-active configs as `supported=false` evidence rather than silently selecting them.
|
|
99
|
+
- Exact `api-id` must match `projects/{inputProject}/locations/global/apis/{api}/configs/{config}` and is fetched directly; cross-project or non-config names fail before network export.
|
|
100
|
+
- Export with `GET https://apigateway.googleapis.com/v1/{name}?view=FULL`. Decode `openapiDocuments[].document.contents` from base64, enforce at most 10 MiB decoded, validate version and non-empty `paths`, and preserve source JSON/YAML with one trailing newline.
|
|
101
|
+
- Exactly one valid OpenAPI document is exportable. Zero or multiple OpenAPI documents are unsupported/manual review; gRPC/service-config files are never converted.
|
|
102
|
+
|
|
103
|
+
Acceptance:
|
|
104
|
+
|
|
105
|
+
- **GCP-GATEWAY-001:** paged APIs/configs yield stable candidates and state/support classifications.
|
|
106
|
+
- **GCP-GATEWAY-002:** FULL export decodes valid JSON and YAML original bytes with correct filename/format.
|
|
107
|
+
- **GCP-GATEWAY-003:** malformed base64, oversize, invalid/empty spec, zero/multiple docs, and gRPC-only configs write nothing and report precise sanitized evidence.
|
|
108
|
+
- **GCP-GATEWAY-004:** explicit cross-project/malformed IDs are rejected; an exact valid ID bypasses list calls and resolves at confidence 100.
|
|
109
|
+
|
|
110
|
+
### R4 - Cloud Endpoints original-source provider (P0)
|
|
111
|
+
|
|
112
|
+
- List only managed services with exact `producerProjectId=<inputProject>`, with pagination.
|
|
113
|
+
- List each service's configs newest-first. v1 candidate set contains the newest config per service; exact IDs can target an older config.
|
|
114
|
+
- Export `services/{serviceName}/configs/{configId}?view=FULL` and inspect only `sourceInfo.sourceFiles` whose `fileType` is `OPEN_API_JSON` or `OPEN_API_YAML`.
|
|
115
|
+
- Decode base64, cap 10 MiB, validate, and preserve one source document. Zero/multiple valid source files are unsupported/manual review. Service Config normalization is never converted back into guessed OpenAPI.
|
|
116
|
+
|
|
117
|
+
Acceptance:
|
|
118
|
+
|
|
119
|
+
- **GCP-ENDPOINTS-001:** producer filtering, config pagination, newest-per-service selection, and stable IDs are exact.
|
|
120
|
+
- **GCP-ENDPOINTS-002:** FULL sourceInfo JSON/YAML exports preserve valid bytes and format.
|
|
121
|
+
- **GCP-ENDPOINTS-003:** normalized config without sourceInfo, zero/multiple OpenAPI files, malformed/oversize/invalid bytes, and foreign producer project write nothing.
|
|
122
|
+
- **GCP-ENDPOINTS-004:** exact older config ID resolves directly while automatic discovery uses newest only.
|
|
123
|
+
|
|
124
|
+
### R5 - Repository and GCP IaC discovery (P0)
|
|
125
|
+
|
|
126
|
+
- Direct repository OpenAPI/Swagger files retain highest precedence after any explicit `api-id` override.
|
|
127
|
+
- Scan at most depth 6 and 200 `.tf`, `.json`, `.yaml`, `.yml` files in lexical order, excluding `.git`, `node_modules`, `dist`, and `output-dir`; do not execute Terraform, gcloud, hooks, or source code.
|
|
128
|
+
- Terraform recognizes only literal `google_api_gateway_api_config` OpenAPI document `path` references and `google_endpoints_service` literal OpenAPI file/content references. Resolve files beneath repo root only.
|
|
129
|
+
- One valid referenced document resolves `iac-embedded`; multiple remain `manual-review`; no match falls through. Extract resource/API/service names only as narrowing fingerprints.
|
|
130
|
+
|
|
131
|
+
Acceptance:
|
|
132
|
+
|
|
133
|
+
- **GCP-IAC-001:** exact Terraform fixtures yield the referenced valid docs and fingerprints without process/network calls.
|
|
134
|
+
- **GCP-IAC-002:** one document resolves, two are ambiguous with no write, zero falls through.
|
|
135
|
+
- **GCP-IAC-003:** traversal, symlink escape, depth/file caps, ignored directories, and lexical repeatability are enforced.
|
|
136
|
+
- **GCP-IAC-004:** interpolation, remote URLs, data sources, provisioners, and nonliteral expressions are evidence-only or ignored and never executed/fetched.
|
|
137
|
+
|
|
138
|
+
### R5b - API Hub, Application Integration, and Integration Connectors providers (P0)
|
|
139
|
+
|
|
140
|
+
- `api-hub`: walk provisioned hub locations, then `apis -> versions -> specs`; export verbatim bytes via `specs/{spec}:contents`; record the full spec resource name in `api-id` and accept it as an explicit `api-id` input. Non-OpenAPI spec types (proto, WSDL, MCP) stay unsupported/manual-review. Product-specific IAM (`roles/apihub.viewer`) or an unprovisioned hub probes `skipped:iam` fail-soft.
|
|
141
|
+
- `app-integration`: list regional integrations whose ACTIVE version has an `api_trigger/`; generate OpenAPI 3.0 server-side with `locations/{location}:generateOpenApiSpec`; evidence marks the contract as generated, never an uploaded source file.
|
|
142
|
+
- `connectors-custom`: list global custom connectors and versions; fetch only `gs://` `specLocation` objects through the authenticated Cloud Storage JSON API; `https://` locations stay manual-review because the action never fetches arbitrary remote URLs.
|
|
143
|
+
|
|
144
|
+
Acceptance:
|
|
145
|
+
|
|
146
|
+
- **GCP-APIHUB-001..005:** strict spec-name parsing, fail-soft probes, verbatim OpenAPI export, unsupported non-OpenAPI types, and project-scoped explicit `api-id` are pinned by tests.
|
|
147
|
+
- **GCP-APPINT-001..003:** fail-soft probes, trigger-scoped listing plus generated-contract export evidence, and non-integration `api-id` yielding no candidates are pinned by tests.
|
|
148
|
+
- **GCP-CONNECTORS-001..004:** strict `gs://` parsing, fail-soft probes, authenticated storage fetch, and https manual-review with no fetch are pinned by tests.
|
|
149
|
+
|
|
150
|
+
### R5c - Apigee Portal, Vertex AI Extensions, and Dialogflow CX Tools providers (P0)
|
|
151
|
+
|
|
152
|
+
- `apigee-portal`: list integrated-portal sites and their `apidocs`; fetch each documentation snapshot via `getDocumentation` and decode the stored OpenAPI contents. Candidates carry `api-id` `organizations/{org}/sites/{site}/apidocs/{document}` and source type `apigee-portal-doc`. Missing portal provisioning or product IAM probes `skipped:iam` fail-soft.
|
|
153
|
+
- `vertex-extensions`: list Vertex AI Extensions in the project region; export each extension's stored `apiSpec` manifest (inline YAML or `gs://` object fetched through the authenticated Cloud Storage JSON API) as source type `vertex-extension-manifest`.
|
|
154
|
+
- `dialogflow-tools`: walk Dialogflow CX agents and their OpenAPI-typed tools; export each tool's stored `openApiSpec.textSchema` verbatim as source type `dialogflow-tool-schema`. Non-OpenAPI tool types stay unsupported/manual-review.
|
|
155
|
+
|
|
156
|
+
Acceptance:
|
|
157
|
+
|
|
158
|
+
- **GCP-PORTAL-001..003:** fail-soft probes, apidoc listing plus documentation export, and non-OpenAPI documentation manual-review are pinned by tests.
|
|
159
|
+
- **GCP-VERTEX-001..003:** fail-soft probes, inline and `gs://` manifest export, and strict location parsing are pinned by tests.
|
|
160
|
+
- **GCP-DIALOGFLOW-001..003:** fail-soft probes, OpenAPI tool export, and non-OpenAPI tool types unsupported are pinned by tests.
|
|
161
|
+
|
|
162
|
+
### R6 - Resolution, narrowing, modes, UX, and telemetry (P0)
|
|
163
|
+
|
|
164
|
+
- Provider order is API Gateway, Cloud Endpoints, Apigee, API Hub, Application Integration, Integration Connectors, Apigee Portal, Vertex AI Extensions, Vertex AI Agent Engines, Dialogflow CX Tools, CES Toolsets, IaC local after direct repo-spec precedence.
|
|
165
|
+
- Narrowing order is `iac-fingerprint`, `project-correlation`, `label-prefilter`, `naming-heuristic`. It partitions without deleting; cap applies after partition. Only explicit ID or one exact label key `postman-repo` with the canonical repo-label value selects. Canonicalization lowercases the full owner/repo slug, replaces `/` with `--`, replaces every other run outside `[a-z0-9_-]` with `-`, trims leading/trailing separators, and declines label selection when the result is empty or exceeds 63 characters.
|
|
166
|
+
- Equal top confidence, multiple local docs, unsupported selected candidates, or no candidate resolve to sanitized manual review, never arbitrary first-item selection.
|
|
167
|
+
- `discover-many` exports every supported candidate in stable order and records attempted/exported/failed/skipped accurately.
|
|
168
|
+
- Ambiguity populates deterministic `candidates-json` and a `## Postman GCP spec discovery` Step Summary. Summary failure is warn-only.
|
|
169
|
+
- Emit at most one best-effort telemetry completion event with `action: 'gcp-spec-discovery'`; no GCP identifiers/specs/URLs/tags enter telemetry.
|
|
170
|
+
|
|
171
|
+
Acceptance:
|
|
172
|
+
|
|
173
|
+
- **GCP-NARROW-001:** tier order, zero-intersection fallthrough, partition-never-delete, exact-label-only select, and cap-after-partition are proven.
|
|
174
|
+
- **GCP-RESOLVE-001:** repo spec/local IaC/cloud precedence, ties, unsupported candidates, explicit IDs, and both modes match contracts.
|
|
175
|
+
- **GCP-SUMMARY-001:** golden summary/candidates are stable and contain no project number, service-account email, full resource name, token, or URL query.
|
|
176
|
+
- **GCP-ENTRY-001:** Action and CLI call one runtime and return all 22 byte-equal outputs.
|
|
177
|
+
- **GCP-TELEMETRY-001:** success/failure/transport-failure/opt-out emit at most one safe event without changing runtime status.
|
|
178
|
+
|
|
179
|
+
### R7 - Documentation, CI, live validation, and release (P0)
|
|
180
|
+
|
|
181
|
+
- README tables are generated from the contract; docs state exact providers, ADC/WIF setup, IAM permissions, and deferred surfaces without overclaiming.
|
|
182
|
+
- CI is one Node 24 `gate` job with one `npm ci`, one bundle, max two concurrent local checks, read-only dist assertion, and no credentialed live step on pull requests.
|
|
183
|
+
- Live runner uses explicit `GCP_PROJECT_ID=dans-project-491920`, requires `--provision --teardown`, provisions collision-resistant run-marked API Gateway API/config, Cloud Endpoints service/config, and Apigee proxy resources, validates the current compiled CLI, and tears down exact run-scoped identities in `finally`. Apigee teardown verifies remote ownership before deletion and refuses foreign resources.
|
|
184
|
+
- Live cases are exactly `gateway-explicit-api-id`, `gateway-discovery`, `gateway-repo-label`, `gateway-label-conflict`, `endpoints-explicit-api-id`, `endpoints-discovery`, `apigee-discovery`, `discover-many`, `iac-single`, and `ambiguity`. Evidence stores only case name/status/source/provider/format and totals.
|
|
185
|
+
- Release accepts only immutable `v1.0.0` at `origin/main`, runs all gates, publishes npm with provenance, creates GitHub release, then moves only `v1` and `v1.0`. Rolling tags never publish npm.
|
|
186
|
+
|
|
187
|
+
Acceptance:
|
|
188
|
+
|
|
189
|
+
- **GCP-DOCS-001:** generated tables have no drift and docs name exactly the v1 providers/non-goals.
|
|
190
|
+
- **GCP-CI-001:** workflow contract test and actionlint pass.
|
|
191
|
+
- **GCP-LIVE-001:** all current-build live cases pass in Dan's project; teardown leaves no current-run active API Gateway/Endpoints resource.
|
|
192
|
+
- **GCP-LIVE-002:** failure/marker mismatch tests prove `finally` cleanup and deletion refusal for foreign resources.
|
|
193
|
+
- **GCP-LIVE-003:** committed evidence totals match and contains no forbidden identifiers/URLs/secrets/spec bodies.
|
|
194
|
+
- **GCP-RELEASE-001:** release workflow tests prove tag/package/main equality, npm idempotence, GitHub release, and dual rolling aliases.
|
|
195
|
+
- **GCP-RELEASE-002:** after publication, npm version, GitHub release, immutable/rolling tag peel, and npm/tag `dist` bytes all verify independently.
|
|
196
|
+
|
|
197
|
+
## Must Pass
|
|
198
|
+
|
|
199
|
+
From `cse/postman-gcp-spec-discovery-action/` on the release candidate:
|
|
200
|
+
|
|
201
|
+
```sh
|
|
202
|
+
npm test
|
|
203
|
+
npm run typecheck
|
|
204
|
+
npm run lint
|
|
205
|
+
npm run build
|
|
206
|
+
npm run verify:dist
|
|
207
|
+
npm run docs:tables
|
|
208
|
+
git diff --exit-code -- README.md
|
|
209
|
+
actionlint .github/workflows/*.yml
|
|
210
|
+
node validation/scripts/validate-live-gcp-surfaces.mjs --provision --teardown
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Then judge-sol and judge-fable MUST both return COMPLETE. Release verification MUST prove `v1.0.0 == v1 == v1.0 == origin/main`, npm reports `1.0.0`, the GitHub release exists, and both npm bundle files byte-match the immutable tag.
|
|
214
|
+
|
|
215
|
+
## Must Not
|
|
216
|
+
|
|
217
|
+
- No service-account key or credential JSON input, durable secret, token logging, ambient project inference, or personal project validation.
|
|
218
|
+
- API Hub, Application Integration, and Integration Connectors custom connectors are IN scope as fail-soft providers (`api-hub`, `app-integration`, `connectors-custom`). No Cloud Run/Functions runtime probing, arbitrary remote URL fetch, traffic-derived route synthesis (APIM shadow-API observation), legacy Apigee Registry, gRPC/`google.api.Service` conversion, Terraform/gcloud execution, or cross-action TypeScript import in v1. Apigee is explicitly IN SCOPE and release-blocking: the Dan's-project org and its billing org must be fully working (management plane, data plane, spec surfaces) before v1 ships, at any cost.
|
|
219
|
+
- No arbitrary ambiguity selection, destructive narrowing, cap-before-partition, path escape, stale-dist proof, credentialed PR job, rolling-tag npm publish, immutable-tag movement, or release from a commit other than protected `origin/main`.
|
|
220
|
+
|
|
221
|
+
## Completion Audit
|
|
222
|
+
|
|
223
|
+
Completion requires a prompt-to-artifact checklist mapping every requirement/AC above to current files, test output, live evidence, judge receipts, commit/CI state, and published artifacts. Missing or indirect evidence remains incomplete.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Provider contracts
|
|
2
|
+
|
|
3
|
+
GCP spec discovery probes providers fail-soft in this order: `api-gateway`, `cloud-endpoints`, `apigee`, `api-hub`, `app-integration`, `connectors-custom`, `apigee-portal`, `vertex-extensions`, `agent-engines`, `dialogflow-tools`, `ces-toolsets`, `iac-local`. Authorization failures become `skipped:iam`; other provider failures become `skipped:error`; remaining providers continue.
|
|
4
|
+
|
|
5
|
+
## Repository-association capability
|
|
6
|
+
|
|
7
|
+
Automatic per-repo selection matches the canonical `postman-repo` label against candidate tags. Providers differ in whether the underlying GCP resource can carry labels at all:
|
|
8
|
+
|
|
9
|
+
| Capability | Providers | Mechanism |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Label-capable (auto-select works) | `api-gateway`, `apigee`, `api-hub`, `connectors-custom` | Resource labels flow into candidate tags (`postman-repo=<owner--repo>` on the API/config, proxy, hub attribute, or connector version) |
|
|
12
|
+
| Label-capable via IaC | `iac-local` | Repository-committed IaC fingerprinting selects without cloud labels |
|
|
13
|
+
| Label-incapable (never auto-selects by label) | `cloud-endpoints`, `app-integration`, `apigee-portal`, `vertex-extensions`, `agent-engines`, `dialogflow-tools`, `ces-toolsets` | The GCP surface exposes no usable label; select with `api-id`, `expected-api-ids-json`, or `service-mapping-json` + `expected-service-name` |
|
|
14
|
+
|
|
15
|
+
Label-incapable providers still participate in discovery and ranking; they simply cannot be *authorized* for export by repository association alone. See [repository-association.md](repository-association.md).
|
|
16
|
+
|
|
17
|
+
## `api-gateway`
|
|
18
|
+
|
|
19
|
+
- Lists API Gateway APIs and configs in the requested project and `global` location.
|
|
20
|
+
- Exports the complete `openapiDocuments` content from the selected config.
|
|
21
|
+
- Produces `api-gateway-config` candidates and records the full config resource name in `api-id`.
|
|
22
|
+
|
|
23
|
+
## `cloud-endpoints`
|
|
24
|
+
|
|
25
|
+
- Lists Cloud Endpoints services and available configs in the requested project.
|
|
26
|
+
- Reads `sourceInfo` entries with `OPEN_API_*` source types to recover the OpenAPI source.
|
|
27
|
+
- Produces `cloud-endpoints-config` candidates and records the config resource name in `api-id`.
|
|
28
|
+
|
|
29
|
+
## `apigee`
|
|
30
|
+
|
|
31
|
+
- Lists accessible Apigee proxies and revisions for the requested project context.
|
|
32
|
+
- Downloads the selected proxy revision zip and extracts OpenAPI documents only from `resources/oas` or `resources/openapi`.
|
|
33
|
+
- Produces `apigee-proxy` candidates and records the proxy revision resource name in `api-id`.
|
|
34
|
+
- Enumerates environments and exports validated `type=oas` resource files as `apigee-env-oas` candidates.
|
|
35
|
+
- A proxy without a supported embedded document is surfaced for manual review; the action does not synthesize routes or convert gRPC definitions.
|
|
36
|
+
|
|
37
|
+
## `api-hub`
|
|
38
|
+
|
|
39
|
+
- Walks the provisioned API Hub locations, then `apis -> versions -> specs`, in the requested project.
|
|
40
|
+
- Exports verbatim spec bytes with `specs/{spec}:contents`; API Hub is the consolidation layer for manually registered specs and Apigee / API Gateway plugin ingestion.
|
|
41
|
+
- Produces `api-hub-spec` candidates and records the full spec resource name in `api-id`.
|
|
42
|
+
- Non-OpenAPI spec types (proto, WSDL, MCP) surface as unsupported candidates for manual review; nothing is converted.
|
|
43
|
+
- IAM note: API Hub uses product-specific roles (`roles/apihub.viewer`); project Owner alone is not sufficient, so unprovisioned or unauthorized hubs probe as `skipped:iam` and discovery continues.
|
|
44
|
+
|
|
45
|
+
## `app-integration`
|
|
46
|
+
|
|
47
|
+
- Lists regional Application Integration integrations and keeps only those whose ACTIVE version has an `api_trigger/` trigger.
|
|
48
|
+
- Calls `locations/{location}:generateOpenApiSpec` to have Google generate the OpenAPI 3.0 contract from the trigger request/response schemas.
|
|
49
|
+
- Produces `app-integration-trigger` candidates; the export evidence marks the contract as generated, not an uploaded source file.
|
|
50
|
+
|
|
51
|
+
## `connectors-custom`
|
|
52
|
+
|
|
53
|
+
- Lists global custom Integration Connectors and their versions; each version records the `specLocation` of the OpenAPI document it was built from.
|
|
54
|
+
- Fetches `gs://` spec objects through the authenticated Cloud Storage JSON API; `https://` locations are surfaced as manual review because the action never fetches arbitrary remote URLs.
|
|
55
|
+
- Produces `connectors-custom-spec` candidates.
|
|
56
|
+
- Lists regional connections and, when schema metadata is available, derives an OpenAPI 3.0 document as `connectors-generated-spec`. Evidence explicitly identifies generated contracts, which rank below stored specification sources; connections without schema metadata are skipped fail-soft.
|
|
57
|
+
|
|
58
|
+
## `apigee-portal`
|
|
59
|
+
|
|
60
|
+
- Lists Apigee portal sites and their published API documents in the requested project-ID organization.
|
|
61
|
+
- Exports the original `oasDocumentation.spec.contents` document.
|
|
62
|
+
- Produces `apigee-portal-doc` candidates whose `api-id` is `organizations/{org}/sites/{site}/apidocs/{document}`.
|
|
63
|
+
- GraphQL, AsyncAPI, missing, and invalid OpenAPI documentation remains visible as unsupported for manual review.
|
|
64
|
+
- IAM note: authorization failures probing portal sites become `skipped:iam`, and discovery continues.
|
|
65
|
+
|
|
66
|
+
## `vertex-extensions`
|
|
67
|
+
|
|
68
|
+
- Enumerates Vertex AI project locations and lists extensions in each region.
|
|
69
|
+
- Exports the extension manifest's inline `openApiYaml` or fetches its `openApiGcsUri` through authenticated Cloud Storage.
|
|
70
|
+
- Produces `vertex-extension-manifest` candidates whose `api-id` is the full extension resource name.
|
|
71
|
+
- Manifests with neither source, multiple sources, invalid OpenAPI, or invalid storage locations remain unsupported.
|
|
72
|
+
- IAM note: authorization failures probing Vertex AI extensions become `skipped:iam`, and discovery continues.
|
|
73
|
+
|
|
74
|
+
## `agent-engines`
|
|
75
|
+
|
|
76
|
+
- Enumerates the same Vertex AI project regions used for extensions and lists `reasoningEngines` in each region.
|
|
77
|
+
- Assembles an OpenAPI 3.0 document from each engine's `spec.classMethods` declarations as `agent-engine-generated-spec`.
|
|
78
|
+
- Empty declarations produce no candidate; operation-less or invalid assembled documents remain unsupported for manual review.
|
|
79
|
+
- Generated Agent Engine specifications carry lower confidence than stored specification sources, and regional probes fail soft.
|
|
80
|
+
|
|
81
|
+
## `dialogflow-tools`
|
|
82
|
+
|
|
83
|
+
- Lists Dialogflow CX agents and their tools in the requested project.
|
|
84
|
+
- Exports the original `openApiSpec.textSchema` stored on each tool.
|
|
85
|
+
- Produces `dialogflow-tool-schema` candidates whose `api-id` is the full tool resource name.
|
|
86
|
+
- Tools without `openApiSpec` are skipped; invalid OpenAPI schemas remain unsupported.
|
|
87
|
+
- IAM note: authorization failures probing Dialogflow become `skipped:iam`, and discovery continues.
|
|
88
|
+
|
|
89
|
+
## `ces-toolsets`
|
|
90
|
+
|
|
91
|
+
- Lists Conversational Agents (Agent Studio) apps and OpenAPI toolsets in the global CES location, then retrieves each toolset's tools with `toolsets/{toolset}:retrieveTools`.
|
|
92
|
+
- Exports inline `openApiToolset.openApiSchema` and tool-level `openApiTool.openApiSchema` documents.
|
|
93
|
+
- Produces `ces-tool-schema` candidates for standalone app tools and `ces-toolset-schema` candidates for toolset-scoped tools; each `api-id` identifies the original resource.
|
|
94
|
+
- Disabled, absent, and unauthorized CES surfaces probe fail-soft so discovery continues.
|
|
95
|
+
|
|
96
|
+
## `iac-local`
|
|
97
|
+
|
|
98
|
+
- Requires no network probe and scans bounded repository IaC files.
|
|
99
|
+
- Recognizes Terraform `google_api_gateway_api_config` path references and `google_endpoints_service` definitions.
|
|
100
|
+
- Resolves embedded documents or repository-relative path references as `iac-embedded` candidates.
|
|
101
|
+
|
|
102
|
+
## Ordering and narrowing
|
|
103
|
+
|
|
104
|
+
A committed `repo-spec` wins before provider probing. All provider candidates enter the same narrowing tiers, in order:
|
|
105
|
+
|
|
106
|
+
1. `iac-fingerprint`
|
|
107
|
+
2. `project-correlation`
|
|
108
|
+
3. `label-prefilter`
|
|
109
|
+
4. `naming-heuristic`
|
|
110
|
+
|
|
111
|
+
The `postman-repo` label is an ownership signal for label prefiltering. The selected tier appears in `narrowing-strategy`. If candidates remain indistinguishable, the action reports ranked evidence for manual review instead of choosing arbitrarily.
|
|
112
|
+
|
|
113
|
+
## Deferred surfaces
|
|
114
|
+
|
|
115
|
+
Runtime URL probing (`Cloud Run`, `GKE`, `Functions`, Firebase hosting, App Engine `_ah/api/discovery` endpoints) is outside v1 because GCP has no catalog of runtime spec endpoints. Traffic-derived surfaces (APIM shadow-API observation jobs, API Hub discovered observations) yield operations, not contracts, and are not spec sources. Convention-only storage (arbitrary GCS buckets, Secret Manager / Parameter Manager payloads, Artifact Registry generic repos, Cloud Build artifacts) and the legacy Apigee Registry (superseded by API Hub) are also excluded. Service Directory holds endpoint routing metadata with no schema payloads. Google API Discovery documents describe Google's own services, not customer APIs. GKE Gateway `HTTPRoute`/Ingress resources carry path skeletons without schemas, and synthesizing routes from them would violate the no-guessed-contract rule. The action does not fetch arbitrary remote URLs, synthesize routes, or convert gRPC descriptor sets (API Gateway `grpcServices`, Cloud Endpoints `FILE_DESCRIPTOR_SET_PROTO`, `google.api.Service` definitions) — descriptor-to-OpenAPI reconstruction produces an inferred contract, not a stored one.
|
|
116
|
+
|
|
117
|
+
IaC scanning is likewise bounded. `iac-local` recognizes Terraform `google_api_gateway_api_config` path references and `google_endpoints_service` definitions only. Other IaC shapes are deferred with rationale: Deployment Manager templates and Config Connector CRDs (`apigateway.cnrm.cloud.google.com`, `servicemanagement.cnrm.cloud.google.com`) embed the same config payloads but require YAML/Jinja evaluation the action does not perform; Pulumi and CDK-for-Terraform programs require executing user code, which is forbidden; Terraform `google_apigee_*` resources reference proxy bundles, not spec documents; `gcloud` invocation scripts and Cloud Build steps are arbitrary shell, not declarative references. Executing Terraform, Deployment Manager, or any user code remains out of scope; only static path or document references committed to the repository are read.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Repository association
|
|
2
|
+
|
|
3
|
+
How a service repository is matched to the deployed GCP resource that owns its API, without `api-id` and without service-name hints. This is the GCP adaptation of the Fox AWS discovery-hub pattern (`postman-cs/fox-postman-onboarding`) — inverted: Fox runs a central hub that scans every AWS gateway and pushes workflows into repos; here each service repo discovers its own spec directly, so there is no hub repository, no cross-repo PAT, and no estate scan.
|
|
4
|
+
|
|
5
|
+
## Marker
|
|
6
|
+
|
|
7
|
+
Label the deployed resource with `postman-repo=<canonical slug>`.
|
|
8
|
+
|
|
9
|
+
Canonicalization of the repository slug (`owner/name`):
|
|
10
|
+
|
|
11
|
+
- lowercase
|
|
12
|
+
- `/` folds to `--`
|
|
13
|
+
- every other character outside `[a-z0-9_-]` folds to `-`
|
|
14
|
+
- leading/trailing `-`/`_` are stripped
|
|
15
|
+
- values longer than 63 characters (the GCP label limit) are rejected, never truncated
|
|
16
|
+
|
|
17
|
+
`github.com/Acme/Payments.API` → `postman-repo=acme--payments-api`.
|
|
18
|
+
|
|
19
|
+
## Where to place the marker
|
|
20
|
+
|
|
21
|
+
| Provider | Resource to label |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `api-gateway` | The API and/or API config (`gcloud api-gateway apis create --labels` / `api-configs create --labels`); config labels override API labels on conflict |
|
|
24
|
+
| `apigee` | The API proxy (proxy `labels`, surfaced through the proxy list with metadata) |
|
|
25
|
+
| `api-hub` | A user-defined string attribute named `postman-repo` on the API/version/spec. API Hub attributes are `map<string, AttributeValues>`, not GCP labels; the action flattens each attribute's first scalar (`stringValues`/`uriValues`/`enumValues`) into candidate tags |
|
|
26
|
+
| `connectors-custom` | The custom connector version labels |
|
|
27
|
+
| `iac-local` | No cloud label needed — committed IaC in the repo fingerprints the API config directly |
|
|
28
|
+
|
|
29
|
+
`cloud-endpoints`, `app-integration`, `apigee-portal`, `vertex-extensions`, `dialogflow-tools`, and `ces-toolsets` expose no usable label surface. Repos served by those providers select with `api-id`, `expected-api-ids-json`, or `service-mapping-json` + `expected-service-name`.
|
|
30
|
+
|
|
31
|
+
Label the resource at deploy time from your IaC (Terraform `labels = { postman-repo = "acme--payments-api" }`) so the association ships with the deployment instead of being hand-applied.
|
|
32
|
+
|
|
33
|
+
## Selection semantics
|
|
34
|
+
|
|
35
|
+
Precedence: explicit `api-id` → spec committed in the repo (`repo-spec`) → single local IaC reference → repository association.
|
|
36
|
+
|
|
37
|
+
- Exactly one conflict-free exact `postman-repo` match auto-selects and exports.
|
|
38
|
+
- Zero matches: `unresolved` (`manual-review`); the evidence prints the exact label value the action expected, ready to paste into IaC.
|
|
39
|
+
- Multiple resources carrying the same value: candidates are narrowed and ranked but never auto-selected — `unresolved` (`manual-review`).
|
|
40
|
+
- Colliding canonical values (case-fold or punctuation collisions such as `Org/Payments` vs `org/payments`, or `org/pay.ments` vs `org/pay-ments`, or slugs past 63 characters): affected repos must set `api-id` or `expected-api-ids-json` explicitly.
|
|
41
|
+
- Generic `repo`/`repository`/`service` labels and name heuristics only reorder candidates; they never authorize an export.
|
|
42
|
+
|
|
43
|
+
## Trust model
|
|
44
|
+
|
|
45
|
+
`postman-repo` is an owner assertion, not a verified binding. Anyone with update permission on the resource can point it at any repository slug. Mitigations:
|
|
46
|
+
|
|
47
|
+
- Keep label-write IAM (`apigateway.apiconfigs.update`, `apigee.proxies.update`, and friends) scoped to the deployment pipeline.
|
|
48
|
+
- Ambiguity always degrades to `manual-review`; a second resource claiming the same repo can redirect nothing silently.
|
|
49
|
+
- The exported spec is written into the calling repository's workspace only; a hostile label can at worst feed that repo its own gateway's spec.
|
|
50
|
+
|
|
51
|
+
## Per-service-repo workflow (spoke)
|
|
52
|
+
|
|
53
|
+
[`templates/postman-gcp-onboard.yml`](../templates/postman-gcp-onboard.yml) is a ready-to-distribute workflow for service repos:
|
|
54
|
+
|
|
55
|
+
1. `google-github-actions/auth` with Workload Identity Federation (org-level variables `GCP_WORKLOAD_IDENTITY_PROVIDER`, `GCP_SERVICE_ACCOUNT`, `GCP_PROJECT_ID`; keyless, no exported SA keys).
|
|
56
|
+
2. This action with `project-id` and an explicit `repo-slug: ${{ github.repository }}` (the same value `GITHUB_REPOSITORY` would supply by default).
|
|
57
|
+
3. A guard step that fails the run when `resolution-status != resolved`, echoing the resolution evidence (including the expected label) instead of guessing.
|
|
58
|
+
4. `postman-cs/postman-api-onboarding-action` with the resolved `spec-path`/`service-name` and the org-level `POSTMAN_API_KEY` secret.
|
|
59
|
+
|
|
60
|
+
Triggers: push to spec/IaC paths, weekly schedule (catches gateway-side drift with no repo change), `workflow_dispatch`, and self-bootstrap on the workflow file itself so onboarding runs the moment the file merges.
|
|
61
|
+
|
|
62
|
+
## Differences from Fox (AWS)
|
|
63
|
+
|
|
64
|
+
| | Fox (AWS) | This action (GCP) |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| Topology | Central hub repo scans gateways, distributes workflows via PRs | Hub-less; each repo self-discovers |
|
|
67
|
+
| Mapping source | `GithubOrg`/`GithubRepo` gateway tags read by the hub | `postman-repo` canonical label read by the repo's own workflow |
|
|
68
|
+
| Spec source of truth | Spec committed in the repo (no AWS export) | Deployed gateway/proxy config exported on demand (repo spec still wins when present) |
|
|
69
|
+
| Credentials | Org secret `POSTMAN_API_KEY` only | Same, plus keyless WIF for GCP reads |
|
|
70
|
+
| Drift | On spec-change push | Push + weekly re-discovery against the live gateway |
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@postman-cse/onboarding-gcp-spec-discovery",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/postman-cs/postman-gcp-spec-discovery-action"
|
|
7
|
+
},
|
|
8
|
+
"description": "Discover GCP-hosted API specs and hand the result to Postman API onboarding.",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "dist/index.cjs",
|
|
11
|
+
"bin": {
|
|
12
|
+
"postman-gcp-spec-discovery": "dist/cli.cjs"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"action.yml",
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"docs",
|
|
19
|
+
"SECURITY.md",
|
|
20
|
+
"SUPPORT.md",
|
|
21
|
+
"RELEASE_POLICY.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"prepare": "git config core.hooksPath .githooks",
|
|
25
|
+
"bundle": "rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node24 --format=cjs --outfile=dist/index.cjs && esbuild src/cli.ts --bundle --platform=node --target=node24 --format=cjs --banner:js='#!/usr/bin/env node' --outfile=dist/cli.cjs && chmod 755 dist/cli.cjs",
|
|
26
|
+
"build": "npm run typecheck && npm run bundle",
|
|
27
|
+
"verify:dist:assert": "git diff --ignore-space-at-eol --text --exit-code -- dist && node scripts/verify-dist-artifact.mjs",
|
|
28
|
+
"verify:dist": "npm run build && npm run verify:dist:assert",
|
|
29
|
+
"docs:tables": "node scripts/render-action-tables.mjs",
|
|
30
|
+
"lint": "eslint .",
|
|
31
|
+
"lint:fix": "eslint . --fix",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"github-action",
|
|
37
|
+
"postman",
|
|
38
|
+
"gcp",
|
|
39
|
+
"google-cloud",
|
|
40
|
+
"api-gateway",
|
|
41
|
+
"cloud-endpoints",
|
|
42
|
+
"openapi",
|
|
43
|
+
"api-onboarding"
|
|
44
|
+
],
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@actions/core": "3.0.1",
|
|
51
|
+
"@postman-cse/automation-telemetry-core": "0.2.0",
|
|
52
|
+
"google-auth-library": "10.9.0",
|
|
53
|
+
"yaml": "2.9.0"
|
|
54
|
+
},
|
|
55
|
+
"overrides": {
|
|
56
|
+
"uuid": "11.1.1"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@eslint/js": "^9.17.0",
|
|
60
|
+
"@types/node": "^24.0.0",
|
|
61
|
+
"esbuild": "0.28.1",
|
|
62
|
+
"eslint": "^9.17.0",
|
|
63
|
+
"typescript": "^5.7.0",
|
|
64
|
+
"typescript-eslint": "^8.18.0",
|
|
65
|
+
"vitest": "^4.0.0"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=24"
|
|
69
|
+
}
|
|
70
|
+
}
|