@stormwateriq/api-client 0.0.1
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 +201 -0
- package/README.md +75 -0
- package/dist/client.d.ts +34 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +67 -0
- package/dist/generated/client/client.gen.d.ts +3 -0
- package/dist/generated/client/client.gen.d.ts.map +1 -0
- package/dist/generated/client/client.gen.js +216 -0
- package/dist/generated/client/index.d.ts +10 -0
- package/dist/generated/client/index.d.ts.map +1 -0
- package/dist/generated/client/index.js +6 -0
- package/dist/generated/client/types.gen.d.ts +121 -0
- package/dist/generated/client/types.gen.d.ts.map +1 -0
- package/dist/generated/client/types.gen.js +2 -0
- package/dist/generated/client/utils.gen.d.ts +38 -0
- package/dist/generated/client/utils.gen.d.ts.map +1 -0
- package/dist/generated/client/utils.gen.js +228 -0
- package/dist/generated/client.gen.d.ts +13 -0
- package/dist/generated/client.gen.d.ts.map +1 -0
- package/dist/generated/client.gen.js +3 -0
- package/dist/generated/core/auth.gen.d.ts +26 -0
- package/dist/generated/core/auth.gen.d.ts.map +1 -0
- package/dist/generated/core/auth.gen.js +14 -0
- package/dist/generated/core/bodySerializer.gen.d.ts +26 -0
- package/dist/generated/core/bodySerializer.gen.d.ts.map +1 -0
- package/dist/generated/core/bodySerializer.gen.js +57 -0
- package/dist/generated/core/params.gen.d.ts +44 -0
- package/dist/generated/core/params.gen.d.ts.map +1 -0
- package/dist/generated/core/params.gen.js +100 -0
- package/dist/generated/core/pathSerializer.gen.d.ts +34 -0
- package/dist/generated/core/pathSerializer.gen.d.ts.map +1 -0
- package/dist/generated/core/pathSerializer.gen.js +106 -0
- package/dist/generated/core/queryKeySerializer.gen.d.ts +19 -0
- package/dist/generated/core/queryKeySerializer.gen.d.ts.map +1 -0
- package/dist/generated/core/queryKeySerializer.gen.js +92 -0
- package/dist/generated/core/serverSentEvents.gen.d.ts +72 -0
- package/dist/generated/core/serverSentEvents.gen.d.ts.map +1 -0
- package/dist/generated/core/serverSentEvents.gen.js +132 -0
- package/dist/generated/core/types.gen.d.ts +79 -0
- package/dist/generated/core/types.gen.d.ts.map +1 -0
- package/dist/generated/core/types.gen.js +2 -0
- package/dist/generated/core/utils.gen.d.ts +20 -0
- package/dist/generated/core/utils.gen.d.ts.map +1 -0
- package/dist/generated/core/utils.gen.js +87 -0
- package/dist/generated/index.d.ts +3 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/index.js +2 -0
- package/dist/generated/sdk.gen.d.ts +90 -0
- package/dist/generated/sdk.gen.d.ts.map +1 -0
- package/dist/generated/sdk.gen.js +136 -0
- package/dist/generated/types.gen.d.ts +535 -0
- package/dist/generated/types.gen.d.ts.map +1 -0
- package/dist/generated/types.gen.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/retry.d.ts +16 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +31 -0
- package/dist/webhooks.d.ts +28 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +65 -0
- package/package.json +45 -0
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Reference webhook-signature verifier for the StormwaterIQ external API
|
|
4
|
+
* (docs/external-api.md §06). Byte-identical to the server's `verifyWebhook`
|
|
5
|
+
* (packages/api) and the Python SDK — all three are pinned to the shared vectors in
|
|
6
|
+
* fixtures/webhook-signature-vectors.json. Tenants call this rather than re-rolling
|
|
7
|
+
* HMAC code.
|
|
8
|
+
*
|
|
9
|
+
* Header form: `t=<unix_ts>,v1=<hmac_sha256_hex>` over the message `"<t>.<rawBody>"`.
|
|
10
|
+
* The timestamp is inside the signed material, so a tampered timestamp invalidates the
|
|
11
|
+
* signature — that is what lets us reject replays by age without trusting the clock.
|
|
12
|
+
*/
|
|
13
|
+
/** Deliveries older than this (seconds) are rejected as replays (§06). */
|
|
14
|
+
export const SIGNATURE_TOLERANCE_SECONDS = 300;
|
|
15
|
+
function parseSignatureHeader(header) {
|
|
16
|
+
const parts = new Map();
|
|
17
|
+
for (const segment of header.split(",")) {
|
|
18
|
+
const idx = segment.indexOf("=");
|
|
19
|
+
if (idx === -1)
|
|
20
|
+
return null;
|
|
21
|
+
parts.set(segment.slice(0, idx).trim(), segment.slice(idx + 1).trim());
|
|
22
|
+
}
|
|
23
|
+
const tRaw = parts.get("t");
|
|
24
|
+
const v1 = parts.get("v1");
|
|
25
|
+
if (!tRaw || !v1)
|
|
26
|
+
return null;
|
|
27
|
+
if (!/^\d+$/.test(tRaw))
|
|
28
|
+
return null;
|
|
29
|
+
if (!/^[0-9a-f]+$/.test(v1))
|
|
30
|
+
return null;
|
|
31
|
+
return { t: Number(tRaw), v1 };
|
|
32
|
+
}
|
|
33
|
+
function timingSafeHexEqual(a, b) {
|
|
34
|
+
if (a.length !== b.length)
|
|
35
|
+
return false;
|
|
36
|
+
try {
|
|
37
|
+
return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Verify a webhook delivery. Returns `true` only when the signature matches the
|
|
45
|
+
* payload under `secret` AND the signed timestamp is within `toleranceSec` of now.
|
|
46
|
+
* Tampered bodies, wrong secrets, malformed headers, and stale/replayed deliveries
|
|
47
|
+
* all return `false`.
|
|
48
|
+
*
|
|
49
|
+
* @param payload the raw request body string exactly as received
|
|
50
|
+
* @param signature the `X-StormwaterIQ-Signature` header value
|
|
51
|
+
* @param secret the endpoint's signing secret
|
|
52
|
+
*/
|
|
53
|
+
export function verify(payload, signature, secret, opts) {
|
|
54
|
+
const parsed = parseSignatureHeader(signature);
|
|
55
|
+
if (!parsed)
|
|
56
|
+
return false;
|
|
57
|
+
const tolerance = opts?.toleranceSec ?? SIGNATURE_TOLERANCE_SECONDS;
|
|
58
|
+
const now = opts?.nowSec ?? Math.floor(Date.now() / 1000);
|
|
59
|
+
if (Math.abs(now - parsed.t) > tolerance)
|
|
60
|
+
return false;
|
|
61
|
+
const expected = createHmac("sha256", secret)
|
|
62
|
+
.update(`${parsed.t}.${payload}`)
|
|
63
|
+
.digest("hex");
|
|
64
|
+
return timingSafeHexEqual(expected, parsed.v1);
|
|
65
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stormwateriq/api-client",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Official TypeScript client for the StormwaterIQ external API: generated typed SDK over the gateway's OpenAPI contract, plus the reference webhook-signature verifier and automatic retries.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"provenance": true
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"openapi-ts": "openapi-ts",
|
|
30
|
+
"test": "vitest run --config vitest.config.ts",
|
|
31
|
+
"release": "release-it"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@hey-api/openapi-ts": "0.98.1",
|
|
35
|
+
"@release-it/conventional-changelog": "^11.0.1",
|
|
36
|
+
"@stormwateriq/typescript-config": "*",
|
|
37
|
+
"@types/node": "^25.2.2",
|
|
38
|
+
"release-it": "^20.2.0",
|
|
39
|
+
"typescript": "~5.9.2",
|
|
40
|
+
"vitest": "^4.0.18"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@hey-api/client-fetch": "0.13.1"
|
|
44
|
+
}
|
|
45
|
+
}
|