@stable_sea/sdk 0.1.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/README.md +69 -0
- package/dist/client.d.ts +47 -0
- package/dist/client.js +42 -0
- package/dist/gen/schema.d.ts +1861 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/webhooks.d.ts +5 -0
- package/dist/webhooks.js +30 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Stable Sea API SDK
|
|
2
|
+
|
|
3
|
+
Typed Node.js client for the Stable Sea API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @stable_sea/sdk@0.1
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package follows semantic versioning. Pin a minor version for pre-1.0 releases;
|
|
12
|
+
minor releases may contain breaking SDK API changes. The dated API contract is
|
|
13
|
+
independent of the npm package version and must be selected explicitly when you
|
|
14
|
+
create a client:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createStableSeaClient } from "@stable_sea/sdk";
|
|
18
|
+
|
|
19
|
+
const client = createStableSeaClient({
|
|
20
|
+
apiKey: process.env.STABLE_SEA_API_KEY!,
|
|
21
|
+
baseUrl: process.env.STABLE_SEA_BASE_URL!,
|
|
22
|
+
version: "2026-07-23",
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Node.js 20 or later is supported. Obtain the base URL and a credential from your
|
|
27
|
+
Stable Sea contact; the public API is not deployed and credentials are not
|
|
28
|
+
self-service. Use `sk_test_…` only with the development host and `sk_live_…` only
|
|
29
|
+
with the production host. The credential identifies one business and carries
|
|
30
|
+
fixed scopes, so do not send a `Stable-Sea-Account` header.
|
|
31
|
+
|
|
32
|
+
| SDK | API contract | Credentials |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `0.1.x` | `2026-07-23` | `sk_test_…` on development; `sk_live_…` on production |
|
|
35
|
+
|
|
36
|
+
## Idempotent writes
|
|
37
|
+
|
|
38
|
+
Create one mutation handle for one logical write. Its `POST` method always sends
|
|
39
|
+
an `Idempotency-Key`; retry the same handle if the response is lost. Supply a
|
|
40
|
+
key when the retry may continue in another process, and reuse that exact key for
|
|
41
|
+
the same logical operation.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const placeOrder = client.mutation();
|
|
45
|
+
const response = await placeOrder.POST("/v1/orders", { body: order });
|
|
46
|
+
// Retry after a transport failure with the same handle (and therefore same key).
|
|
47
|
+
const retry = await placeOrder.POST("/v1/orders", { body: order });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Webhooks
|
|
51
|
+
|
|
52
|
+
Pass the exact raw request bytes to `verifyWebhookSignature` before parsing the
|
|
53
|
+
body. It verifies HMAC-SHA256 in constant time and rejects signatures outside a
|
|
54
|
+
five-minute window by default. Set `toleranceSeconds` only when your replay policy
|
|
55
|
+
requires a different window.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { verifyWebhookSignature } from "@stable_sea/sdk";
|
|
59
|
+
|
|
60
|
+
const valid = await verifyWebhookSignature(
|
|
61
|
+
rawBody,
|
|
62
|
+
request.headers.get("Stable-Sea-Signature") ?? "",
|
|
63
|
+
process.env.STABLE_SEA_WEBHOOK_SECRET!,
|
|
64
|
+
);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
API request, response, pagination, and error types are generated from
|
|
68
|
+
`services/backend/api/openapi.public.json` and exported as `paths`, `operations`,
|
|
69
|
+
and `components`.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import createClient from "openapi-fetch";
|
|
2
|
+
import type { FetchResponse } from "openapi-fetch";
|
|
3
|
+
import type { paths } from "./gen/schema.js";
|
|
4
|
+
export interface StableSeaClientOptions {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
version?: "2026-07-23";
|
|
8
|
+
}
|
|
9
|
+
type GeneratedClient = ReturnType<typeof createClient<paths>>;
|
|
10
|
+
type MutationPath = {
|
|
11
|
+
[Path in keyof paths]: paths[Path] extends {
|
|
12
|
+
post: unknown;
|
|
13
|
+
} ? Path : never;
|
|
14
|
+
}[keyof paths];
|
|
15
|
+
type MutationOperation<Path extends MutationPath> = paths[Path] extends {
|
|
16
|
+
post: infer Operation;
|
|
17
|
+
} ? Operation : never;
|
|
18
|
+
type MutationParameters<Path extends MutationPath> = MutationOperation<Path> extends {
|
|
19
|
+
parameters: infer Parameters;
|
|
20
|
+
} ? Omit<Parameters, "header"> : never;
|
|
21
|
+
type MutationBody<Path extends MutationPath> = MutationOperation<Path> extends {
|
|
22
|
+
requestBody?: {
|
|
23
|
+
content: {
|
|
24
|
+
"application/json": infer Body;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
} ? Body : never;
|
|
28
|
+
type MutationInit<Path extends MutationPath> = {
|
|
29
|
+
body?: MutationBody<Path>;
|
|
30
|
+
} & ({} extends MutationParameters<Path> ? {
|
|
31
|
+
params?: MutationParameters<Path>;
|
|
32
|
+
} : {
|
|
33
|
+
params: MutationParameters<Path>;
|
|
34
|
+
});
|
|
35
|
+
type MutationResponse<Path extends MutationPath> = Promise<FetchResponse<MutationOperation<Path> & Record<string, unknown>, MutationInit<Path>, "application/json">>;
|
|
36
|
+
export interface StableSeaMutation {
|
|
37
|
+
idempotencyKey: string;
|
|
38
|
+
POST<Path extends MutationPath>(path: Path, init: MutationInit<Path>): MutationResponse<Path>;
|
|
39
|
+
}
|
|
40
|
+
export interface StableSeaMutationOptions {
|
|
41
|
+
idempotencyKey?: string;
|
|
42
|
+
}
|
|
43
|
+
export type StableSeaClient = Omit<GeneratedClient, "POST"> & {
|
|
44
|
+
mutation(options?: StableSeaMutationOptions): StableSeaMutation;
|
|
45
|
+
};
|
|
46
|
+
export declare function createStableSeaClient(options: StableSeaClientOptions): StableSeaClient;
|
|
47
|
+
export {};
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import createClient from "openapi-fetch";
|
|
2
|
+
function mutationKey(options) {
|
|
3
|
+
if (options.idempotencyKey === "")
|
|
4
|
+
throw new Error("idempotencyKey is required");
|
|
5
|
+
return options.idempotencyKey ?? crypto.randomUUID();
|
|
6
|
+
}
|
|
7
|
+
export function createStableSeaClient(options) {
|
|
8
|
+
if (!options.apiKey)
|
|
9
|
+
throw new Error("apiKey is required");
|
|
10
|
+
if (!options.baseUrl)
|
|
11
|
+
throw new Error("baseUrl is required");
|
|
12
|
+
const rawClient = createClient({ baseUrl: options.baseUrl });
|
|
13
|
+
rawClient.use({
|
|
14
|
+
onRequest({ request }) {
|
|
15
|
+
request.headers.set("Authorization", `Bearer ${options.apiKey}`);
|
|
16
|
+
// No Stable-Sea-Account. The API is single-tenant: the credential names
|
|
17
|
+
// the business, and every operation refuses that header with a coded 400.
|
|
18
|
+
// Sending it would brick every call the client makes.
|
|
19
|
+
if (options.version)
|
|
20
|
+
request.headers.set("Stable-Sea-Version", options.version);
|
|
21
|
+
return request;
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
const { POST, ...client } = rawClient;
|
|
25
|
+
const post = POST;
|
|
26
|
+
return Object.assign(client, {
|
|
27
|
+
mutation(mutationOptions = {}) {
|
|
28
|
+
const idempotencyKey = mutationKey(mutationOptions);
|
|
29
|
+
const idempotentPost = (path, init) => post(path, {
|
|
30
|
+
...init,
|
|
31
|
+
params: {
|
|
32
|
+
...(init.params ?? {}),
|
|
33
|
+
header: { "Idempotency-Key": idempotencyKey },
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
idempotencyKey,
|
|
38
|
+
POST: idempotentPost,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|